├── .gitignore ├── Android ├── app │ ├── .gitignore │ ├── libs │ │ └── PLACEHOLDER │ ├── src │ │ ├── main │ │ │ ├── assets │ │ │ │ └── mixing.mp3 │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_ban.png │ │ │ │ │ ├── ic_close.png │ │ │ │ │ ├── ic_exit.png │ │ │ │ │ ├── ic_gift.png │ │ │ │ │ ├── ic_join.png │ │ │ │ │ ├── ic_effect.png │ │ │ │ │ ├── ic_mic_off.png │ │ │ │ │ ├── ic_mic_on.png │ │ │ │ │ ├── ic_unkown.png │ │ │ │ │ ├── ic_audience.png │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_mixing_off.png │ │ │ │ │ ├── ic_mixing_on.png │ │ │ │ │ ├── ic_speaker_on.png │ │ │ │ │ ├── ic_speaker_off.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ ├── ic_mic_off_little.png │ │ │ │ │ └── ic_mic_on_little.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── bg_channel_0.jpg │ │ │ │ │ ├── bg_channel_1.jpg │ │ │ │ │ ├── bg_channel_2.jpg │ │ │ │ │ ├── bg_channel_3.jpg │ │ │ │ │ ├── bg_channel_4.jpg │ │ │ │ │ ├── img_avatar_0.jpg │ │ │ │ │ ├── img_avatar_1.jpg │ │ │ │ │ ├── img_avatar_10.jpg │ │ │ │ │ ├── img_avatar_11.jpg │ │ │ │ │ ├── img_avatar_2.jpg │ │ │ │ │ ├── img_avatar_3.png │ │ │ │ │ ├── img_avatar_4.jpg │ │ │ │ │ ├── img_avatar_5.jpg │ │ │ │ │ ├── img_avatar_6.jpg │ │ │ │ │ ├── img_avatar_7.jpg │ │ │ │ │ ├── img_avatar_8.jpg │ │ │ │ │ ├── img_avatar_9.jpg │ │ │ │ │ ├── img_channel_0.png │ │ │ │ │ ├── img_channel_1.png │ │ │ │ │ ├── img_channel_2.png │ │ │ │ │ ├── img_channel_3.png │ │ │ │ │ ├── img_channel_4.png │ │ │ │ │ ├── bg_channel_list.jpg │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── drawable │ │ │ │ │ ├── bg_seat_grid_item.xml │ │ │ │ │ ├── bg_member_dialog.xml │ │ │ │ │ ├── bg_radius_container.xml │ │ │ │ │ ├── selector_mixing.xml │ │ │ │ │ ├── selector_changer_rb.xml │ │ │ │ │ ├── bg_channel_grid_item.xml │ │ │ │ │ ├── bg_member_item_btn.xml │ │ │ │ │ ├── bg_seat_item.xml │ │ │ │ │ ├── inset_divider.xml │ │ │ │ │ ├── bg_gift_container.xml │ │ │ │ │ ├── bg_changer_item_selected.xml │ │ │ │ │ ├── bg_changer_item_normal.xml │ │ │ │ │ ├── bg_changer_rb_checked.xml │ │ │ │ │ ├── bg_changer_title.xml │ │ │ │ │ └── bg_changer_rb_normal.xml │ │ │ │ ├── color │ │ │ │ │ └── selector_changer_rb.xml │ │ │ │ ├── values │ │ │ │ │ ├── ids.xml │ │ │ │ │ ├── styles.xml │ │ │ │ │ ├── colors.xml │ │ │ │ │ ├── strings_config.xml │ │ │ │ │ ├── arrays.xml │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── dimens.xml │ │ │ │ ├── layout │ │ │ │ │ ├── layout_item_changer.xml │ │ │ │ │ ├── layout_item_channel.xml │ │ │ │ │ ├── activity_channel_grid.xml │ │ │ │ │ ├── layout_item_seat.xml │ │ │ │ │ ├── layout_item_message.xml │ │ │ │ │ ├── dialog_member_list.xml │ │ │ │ │ ├── dialog_changer_grid.xml │ │ │ │ │ ├── layout_gift.xml │ │ │ │ │ ├── layout_item_member.xml │ │ │ │ │ ├── activity_chat_room.xml │ │ │ │ │ └── layout_chat_room_bottom.xml │ │ │ │ └── values-zh │ │ │ │ │ └── strings.xml │ │ │ ├── java │ │ │ │ └── io │ │ │ │ │ └── agora │ │ │ │ │ └── chatroom │ │ │ │ │ ├── manager │ │ │ │ │ ├── MessageManager.java │ │ │ │ │ └── ChatRoomEventListener.java │ │ │ │ │ ├── model │ │ │ │ │ ├── Constant.java │ │ │ │ │ ├── Seat.java │ │ │ │ │ ├── AttributeKey.java │ │ │ │ │ ├── Channel.java │ │ │ │ │ ├── Member.java │ │ │ │ │ └── Message.java │ │ │ │ │ ├── ChatRoomApplication.java │ │ │ │ │ ├── widget │ │ │ │ │ ├── FixedRatioImageView.java │ │ │ │ │ ├── GiftPopView.java │ │ │ │ │ ├── VoiceChangerGridRecyclerView.java │ │ │ │ │ └── VoiceChangerDialog.java │ │ │ │ │ ├── util │ │ │ │ │ ├── MemberUtil.java │ │ │ │ │ └── AlertUtil.java │ │ │ │ │ ├── activity │ │ │ │ │ └── ChannelGridActivity.java │ │ │ │ │ └── adapter │ │ │ │ │ ├── MessageListAdapter.java │ │ │ │ │ ├── VoiceChangerGridAdapter.java │ │ │ │ │ ├── ChannelGridAdapter.java │ │ │ │ │ └── SeatGridAdapter.java │ │ │ └── AndroidManifest.xml │ │ ├── test │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── agora │ │ │ │ └── chatroom │ │ │ │ └── ExampleUnitTest.java │ │ └── androidTest │ │ │ └── java │ │ │ └── io │ │ │ └── agora │ │ │ └── chatroom │ │ │ └── ExampleInstrumentedTest.java │ └── build.gradle ├── popmenu │ ├── .gitignore │ ├── src │ │ └── main │ │ │ ├── res │ │ │ ├── layout │ │ │ │ └── layout_menu_item.xml │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── attrs.xml │ │ │ │ └── styles.xml │ │ │ └── drawable │ │ │ │ └── bg_menu.xml │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── me │ │ │ └── kareluo │ │ │ └── ui │ │ │ ├── OptionItemView.java │ │ │ ├── PopVerticalScrollView.java │ │ │ └── PopHorizontalScrollView.java │ ├── build.gradle │ └── proguard-rules.pro ├── settings.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── build.gradle ├── .gitignore ├── gradle.properties ├── ci.env.py └── gradlew.bat ├── iOS ├── .gitignore ├── AgoraChatRoom │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── ic_ban.imageset │ │ │ ├── ic_ban.png │ │ │ └── Contents.json │ │ ├── ic_close.imageset │ │ │ ├── ic_close.png │ │ │ └── Contents.json │ │ ├── ic_exit.imageset │ │ │ ├── ic_exit.png │ │ │ └── Contents.json │ │ ├── ic_gift.imageset │ │ │ ├── ic_gift.png │ │ │ └── Contents.json │ │ ├── ic_join.imageset │ │ │ ├── ic_join.png │ │ │ └── Contents.json │ │ ├── ic_create.imageset │ │ │ ├── ic_create.png │ │ │ └── Contents.json │ │ ├── ic_effect.imageset │ │ │ ├── ic_effect.png │ │ │ └── Contents.json │ │ ├── ic_mic_on.imageset │ │ │ ├── ic_mic_on.png │ │ │ └── Contents.json │ │ ├── ic_unkown.imageset │ │ │ ├── ic_unkown.png │ │ │ └── Contents.json │ │ ├── ic_mic_off.imageset │ │ │ ├── ic_mic_off.png │ │ │ └── Contents.json │ │ ├── ic_audience.imageset │ │ │ ├── ic_audience.png │ │ │ └── Contents.json │ │ ├── bg_channel_0.imageset │ │ │ ├── bg_channel_0.jpg │ │ │ └── Contents.json │ │ ├── bg_channel_1.imageset │ │ │ ├── bg_channel_1.jpg │ │ │ └── Contents.json │ │ ├── bg_channel_2.imageset │ │ │ ├── bg_channel_2.jpg │ │ │ └── Contents.json │ │ ├── bg_channel_3.imageset │ │ │ ├── bg_channel_3.jpg │ │ │ └── Contents.json │ │ ├── bg_channel_4.imageset │ │ │ ├── bg_channel_4.jpg │ │ │ └── Contents.json │ │ ├── ic_mixing_off.imageset │ │ │ ├── ic_mixing_off.png │ │ │ └── Contents.json │ │ ├── ic_mixing_on.imageset │ │ │ ├── ic_mixing_on.png │ │ │ └── Contents.json │ │ ├── ic_speaker_on.imageset │ │ │ ├── ic_speaker_on.png │ │ │ └── Contents.json │ │ ├── img_channel_0.imageset │ │ │ ├── img_channel_0.png │ │ │ └── Contents.json │ │ ├── img_channel_1.imageset │ │ │ ├── img_channel_1.png │ │ │ └── Contents.json │ │ ├── img_channel_2.imageset │ │ │ ├── img_channel_2.png │ │ │ └── Contents.json │ │ ├── img_channel_3.imageset │ │ │ ├── img_channel_3.png │ │ │ └── Contents.json │ │ ├── img_channel_4.imageset │ │ │ ├── img_channel_4.png │ │ │ └── Contents.json │ │ ├── img_header_0.imageset │ │ │ ├── img_header_0.jpg │ │ │ └── Contents.json │ │ ├── img_header_1.imageset │ │ │ ├── img_header_1.jpg │ │ │ └── Contents.json │ │ ├── img_header_10.imageset │ │ │ ├── img_header_10.jpg │ │ │ └── Contents.json │ │ ├── img_header_11.imageset │ │ │ ├── img_header_11.jpg │ │ │ └── Contents.json │ │ ├── img_header_2.imageset │ │ │ ├── img_header_2.jpg │ │ │ └── Contents.json │ │ ├── img_header_3.imageset │ │ │ ├── img_header_3.png │ │ │ └── Contents.json │ │ ├── img_header_4.imageset │ │ │ ├── img_header_4.jpg │ │ │ └── Contents.json │ │ ├── img_header_5.imageset │ │ │ ├── img_header_5.jpg │ │ │ └── Contents.json │ │ ├── img_header_6.imageset │ │ │ ├── img_header_6.jpg │ │ │ └── Contents.json │ │ ├── img_header_7.imageset │ │ │ ├── img_header_7.jpg │ │ │ └── Contents.json │ │ ├── img_header_8.imageset │ │ │ ├── img_header_8.jpg │ │ │ └── Contents.json │ │ ├── img_header_9.imageset │ │ │ ├── img_header_9.jpg │ │ │ └── Contents.json │ │ ├── ic_speaker_off.imageset │ │ │ ├── ic_speaker_off.png │ │ │ └── Contents.json │ │ ├── bg_channel_list.imageset │ │ │ ├── bg_channel_list.jpg │ │ │ └── Contents.json │ │ ├── ic_mic_on_little.imageset │ │ │ ├── ic_mic_on_little.png │ │ │ └── Contents.json │ │ ├── ic_mic_off_little.imageset │ │ │ ├── ic_mic_off_little.png │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Sound Horizon - 記憶の水底.mp3 │ ├── Channel.swift │ ├── Seat.swift │ ├── MessageManager.swift │ ├── ChannelCell.swift │ ├── Constant.swift │ ├── AppDelegate.swift │ ├── NavigationController.swift │ ├── KeyCenter.swift │ ├── AttributeKey.swift │ ├── VoiceChangerCell.swift │ ├── zh-Hans.lproj │ │ ├── Main.strings │ │ ├── Localizable.strings │ │ └── LaunchScreen.storyboard │ ├── MessageCell.swift │ ├── Member.swift │ ├── CustomView.swift │ ├── MessageTableViewController.swift │ ├── en.lproj │ │ └── Localizable.strings │ ├── AlertUtil.swift │ ├── Message.swift │ ├── Info.plist │ ├── GiftPopView.swift │ ├── MemberCell.swift │ ├── SeatCollectionViewController.swift │ ├── VoiceChanger.swift │ ├── ChannelViewController.swift │ ├── MemberViewController.swift │ ├── MemberUtil.swift │ └── VoiceChangerViewController.swift ├── AgoraChatRoom.xcodeproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── ci.env.py ├── Image ├── sdk.png ├── appid.jpg ├── sdk.en.png └── appid.en.jpg ├── azure-pipelines.yml └── cicd └── templates ├── build-android.yml └── build-ios.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /Android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Android/popmenu/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Android/app/libs/PLACEHOLDER: -------------------------------------------------------------------------------- 1 | agora-rtc-sdk.jar 2 | -------------------------------------------------------------------------------- /Android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':popmenu' 2 | -------------------------------------------------------------------------------- /iOS/.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.xcuserdatad 3 | *.xcscmblueprint 4 | libs/ -------------------------------------------------------------------------------- /Image/sdk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Image/sdk.png -------------------------------------------------------------------------------- /Image/appid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Image/appid.jpg -------------------------------------------------------------------------------- /Image/sdk.en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Image/sdk.en.png -------------------------------------------------------------------------------- /Image/appid.en.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Image/appid.en.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Android/app/src/main/assets/mixing.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/assets/mixing.mp3 -------------------------------------------------------------------------------- /Android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Sound Horizon - 記憶の水底.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Sound Horizon - 記憶の水底.mp3 -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_ban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_ban.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_close.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_exit.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_gift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_gift.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_join.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_join.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_effect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_effect.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_mic_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_mic_off.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_mic_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_mic_on.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_unkown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_unkown.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_audience.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_audience.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_mixing_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_mixing_off.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_mixing_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_mixing_on.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_speaker_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_speaker_on.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_speaker_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_speaker_off.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/bg_channel_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/bg_channel_0.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/bg_channel_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/bg_channel_1.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/bg_channel_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/bg_channel_2.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/bg_channel_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/bg_channel_3.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/bg_channel_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/bg_channel_4.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_0.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_1.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_10.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_11.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_2.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_3.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_4.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_5.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_6.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_7.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_8.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_avatar_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_avatar_9.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_channel_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_channel_0.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_channel_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_channel_1.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_channel_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_channel_2.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_channel_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_channel_3.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/img_channel_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/img_channel_4.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_mic_off_little.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_mic_off_little.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-hdpi/ic_mic_on_little.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-hdpi/ic_mic_on_little.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/bg_channel_list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/bg_channel_list.jpg -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_ban.imageset/ic_ban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_ban.imageset/ic_ban.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_close.imageset/ic_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_close.imageset/ic_close.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_exit.imageset/ic_exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_exit.imageset/ic_exit.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_gift.imageset/ic_gift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_gift.imageset/ic_gift.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_join.imageset/ic_join.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_join.imageset/ic_join.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_create.imageset/ic_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_create.imageset/ic_create.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_effect.imageset/ic_effect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_effect.imageset/ic_effect.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mic_on.imageset/ic_mic_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_mic_on.imageset/ic_mic_on.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_unkown.imageset/ic_unkown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_unkown.imageset/ic_unkown.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mic_off.imageset/ic_mic_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_mic_off.imageset/ic_mic_off.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_audience.imageset/ic_audience.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_audience.imageset/ic_audience.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_0.imageset/bg_channel_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/bg_channel_0.imageset/bg_channel_0.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_1.imageset/bg_channel_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/bg_channel_1.imageset/bg_channel_1.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_2.imageset/bg_channel_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/bg_channel_2.imageset/bg_channel_2.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_3.imageset/bg_channel_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/bg_channel_3.imageset/bg_channel_3.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_4.imageset/bg_channel_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/bg_channel_4.imageset/bg_channel_4.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mixing_off.imageset/ic_mixing_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_mixing_off.imageset/ic_mixing_off.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mixing_on.imageset/ic_mixing_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_mixing_on.imageset/ic_mixing_on.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_speaker_on.imageset/ic_speaker_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_speaker_on.imageset/ic_speaker_on.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_channel_0.imageset/img_channel_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_channel_0.imageset/img_channel_0.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_channel_1.imageset/img_channel_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_channel_1.imageset/img_channel_1.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_channel_2.imageset/img_channel_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_channel_2.imageset/img_channel_2.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_channel_3.imageset/img_channel_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_channel_3.imageset/img_channel_3.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_channel_4.imageset/img_channel_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_channel_4.imageset/img_channel_4.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_0.imageset/img_header_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_0.imageset/img_header_0.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_1.imageset/img_header_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_1.imageset/img_header_1.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_10.imageset/img_header_10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_10.imageset/img_header_10.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_11.imageset/img_header_11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_11.imageset/img_header_11.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_2.imageset/img_header_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_2.imageset/img_header_2.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_3.imageset/img_header_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_3.imageset/img_header_3.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_4.imageset/img_header_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_4.imageset/img_header_4.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_5.imageset/img_header_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_5.imageset/img_header_5.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_6.imageset/img_header_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_6.imageset/img_header_6.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_7.imageset/img_header_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_7.imageset/img_header_7.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_8.imageset/img_header_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_8.imageset/img_header_8.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_9.imageset/img_header_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/img_header_9.imageset/img_header_9.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_speaker_off.imageset/ic_speaker_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_speaker_off.imageset/ic_speaker_off.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_list.imageset/bg_channel_list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/bg_channel_list.imageset/bg_channel_list.jpg -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mic_on_little.imageset/ic_mic_on_little.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_mic_on_little.imageset/ic_mic_on_little.png -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mic_off_little.imageset/ic_mic_off_little.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Usecase/Chatroom/HEAD/iOS/AgoraChatRoom/Assets.xcassets/ic_mic_off_little.imageset/ic_mic_off_little.png -------------------------------------------------------------------------------- /Android/popmenu/src/main/res/layout/layout_menu_item.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_seat_grid_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_member_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_radius_container.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 28 17:19:53 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip 7 | -------------------------------------------------------------------------------- /Android/popmenu/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7dp 4 | 7dp 5 | -------------------------------------------------------------------------------- /Android/popmenu/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Android/app/src/main/res/color/selector_changer_rb.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/selector_mixing.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Channel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Channel.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/12/5. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct Channel { 12 | var drawableRes: UIImage 13 | var backgroundRes: UIImage 14 | var name: String 15 | } 16 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/selector_changer_rb.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_channel_grid_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /Android/app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_member_item_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_seat_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/inset_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Android/popmenu/src/main/res/drawable/bg_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_ban.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_ban.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_close.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_create.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_create.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_effect.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_effect.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_exit.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_exit.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_gift.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_gift.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_join.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_join.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mic_on.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_mic_on.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_unkown.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_unkown.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_audience.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_audience.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mic_off.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_mic_off.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bg_channel_0.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bg_channel_1.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bg_channel_2.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bg_channel_3.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bg_channel_4.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mixing_off.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_mixing_off.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mixing_on.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_mixing_on.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_speaker_off.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_speaker_off.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_speaker_on.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_speaker_on.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_channel_0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_channel_0.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_channel_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_channel_1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_channel_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_channel_2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_channel_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_channel_3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_channel_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_channel_4.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_0.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_1.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_10.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_11.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_11.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_2.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_4.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_5.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_6.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_7.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_8.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/img_header_9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_header_9.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/bg_channel_list.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bg_channel_list.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mic_on_little.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_mic_on_little.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/ic_mic_off_little.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_mic_off_little.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Seat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Seat.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/22. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Seat: Codable { 12 | var userId: String? 13 | var isClosed: Bool = false 14 | 15 | init(userId: String) { 16 | self.userId = userId 17 | } 18 | 19 | init(isClosed: Bool) { 20 | self.isClosed = isClosed 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_gift_container.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /Android/popmenu/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | version = "1.1.0" 4 | 5 | android { 6 | compileSdkVersion 29 7 | 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion 29 11 | versionCode 110 12 | versionName version 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/MessageManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LXH on 2019/12/11. 3 | // Copyright (c) 2019 CavanSu. All rights reserved. 4 | // 5 | 6 | import Foundation 7 | 8 | import AgoraRtmKit 9 | 10 | protocol MessageManager { 11 | func sendOrder(userId: String, orderType: String, content: String?, callback: AgoraRtmSendPeerMessageBlock?) 12 | 13 | func sendMessage(text: String) 14 | 15 | func processMessage(rtmMessage: AgoraRtmMessage) 16 | 17 | func addMessage(message: Message) 18 | } 19 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/ChannelCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelCell.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/12/5. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ChannelCell: UICollectionViewCell { 12 | @IBOutlet weak var image: UIImageView! 13 | @IBOutlet weak var name: UILabel! 14 | 15 | func update(_ channel: Channel) { 16 | self.image.image = channel.drawableRes 17 | self.name.text = channel.name 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Android/app/src/test/java/io/agora/chatroom/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/manager/MessageManager.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.manager; 2 | 3 | import io.agora.chatroom.model.Message; 4 | import io.agora.rtm.ResultCallback; 5 | import io.agora.rtm.RtmMessage; 6 | 7 | public interface MessageManager { 8 | 9 | void sendOrder(String userId, String orderType, String content, ResultCallback callback); 10 | 11 | void sendMessage(String text); 12 | 13 | void processMessage(RtmMessage rtmMessage); 14 | 15 | void addMessage(Message message); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_changer_item_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Constant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constant.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/27. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Constant { 12 | static let sUserId: UInt = UInt(UInt32(bitPattern: MemberUtil.getUserId())) 13 | 14 | static func isMyself(_ userId: String) -> Bool { 15 | userId == String(sUserId) 16 | } 17 | 18 | static let sName = MemberUtil.getName() 19 | static let sAvatarIndex = MemberUtil.getAvatarIndex() 20 | } 21 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_changer_item_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/layout_item_changer.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/manager/ChatRoomEventListener.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.manager; 2 | 3 | public interface ChatRoomEventListener { 4 | 5 | void onSeatUpdated(int position); 6 | 7 | void onUserGivingGift(String userId); 8 | 9 | void onMessageAdded(int position); 10 | 11 | void onMemberListUpdated(String userId); 12 | 13 | void onUserStatusChanged(String userId, Boolean muted); 14 | 15 | void onAudioMixingStateChanged(boolean isPlaying); 16 | 17 | void onAudioVolumeIndication(String userId, int volume); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/model/Constant.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.model; 2 | 3 | import android.text.TextUtils; 4 | 5 | import io.agora.chatroom.util.MemberUtil; 6 | 7 | public class Constant { 8 | 9 | public static final int sUserId = MemberUtil.getUserId(); 10 | 11 | public static boolean isMyself(String userId) { 12 | return TextUtils.equals(userId, String.valueOf(Constant.sUserId)); 13 | } 14 | 15 | public static final String sName = MemberUtil.getName(); 16 | public static final int sAvatarIndex = MemberUtil.getAvatarIndex(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by CavanSu on 2018/8/15. 6 | // Copyright © 2018 Agora. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | RtcManager.shared.initialize() 17 | RtmManager.shared.initialize() 18 | return true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/ChatRoomApplication.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom; 2 | 3 | import android.app.Application; 4 | 5 | import io.agora.chatroom.manager.RtcManager; 6 | import io.agora.chatroom.manager.RtmManager; 7 | 8 | public class ChatRoomApplication extends Application { 9 | 10 | public static Application instance; 11 | 12 | public ChatRoomApplication() { 13 | instance = this; 14 | } 15 | 16 | @Override 17 | public void onCreate() { 18 | super.onCreate(); 19 | RtcManager.instance(this).init(); 20 | RtmManager.instance(this).init(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.3.2' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | google() 19 | jcenter() 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/NavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationController.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/12/5. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NavigationController: UINavigationController { 12 | override func viewWillAppear(_ animated: Bool) { 13 | navigationBar.setBackgroundImage(UIImage(), for: .default) 14 | navigationBar.shadowImage = UIImage() 15 | } 16 | 17 | override var preferredStatusBarStyle: UIStatusBarStyle { 18 | topViewController?.preferredStatusBarStyle ?? super.preferredStatusBarStyle 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_changer_rb_checked.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_changer_title.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Android/app/src/main/res/drawable/bg_changer_rb_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Android/popmenu/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/model/Seat.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.model; 2 | 3 | public class Seat { 4 | 5 | private String userId; 6 | private boolean isClosed; 7 | 8 | public Seat(String userId) { 9 | this.userId = userId; 10 | } 11 | 12 | public Seat(boolean isClosed) { 13 | this.isClosed = isClosed; 14 | } 15 | 16 | public String getUserId() { 17 | return userId; 18 | } 19 | 20 | public void setUserId(String userId) { 21 | this.userId = userId; 22 | } 23 | 24 | public boolean isClosed() { 25 | return isClosed; 26 | } 27 | 28 | public void setClosed(boolean closed) { 29 | isClosed = closed; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Android/popmenu/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/felix/Workspace/Library/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /Android/.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | build/ 15 | 16 | # Local configuration file (sdk path, etc) 17 | local.properties 18 | 19 | # Eclipse project files 20 | .classpath 21 | .project 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Intellij project files 27 | *.iml 28 | *.ipr 29 | *.iws 30 | .idea/ 31 | 32 | # Mac OS X 33 | .DS_Store 34 | 35 | # Android Studio 36 | .gradle 37 | /local.properties 38 | /.idea/workspace.xml 39 | obj/ 40 | libs/ 41 | 42 | .externalNativeBuild 43 | 44 | # cscope or ctags files 45 | cscope.in.out 46 | cscope.out 47 | cscope.po.out 48 | tags 49 | 50 | -------------------------------------------------------------------------------- /Android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #EEEEEE 4 | #EEEEEE 5 | #CCCCCC 6 | #CCCCCC 7 | #999999 8 | 9 | #333333 10 | #4C9D9C98 11 | #19FFFFFF 12 | #4CFFFFFF 13 | #66FFFFFF 14 | #99FFFFFF 15 | #09BDF4 16 | #202932 17 | #007AFF 18 | 19 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - template: cicd/templates/build-android.yml 3 | parameters: 4 | displayName: 'ChatRoomAndroid' 5 | workingDirectory: 'Android' 6 | rtcSdkUrl: 'https://download.agora.io/sdk/release/Agora_Native_SDK_for_Android_v3_1_0_VOICE.zip?_ga=2.213530388.1493357375.1597372715-269570672.1596526126' 7 | rtmSdkUrl: 'https://download.agora.io/rtmsdk/release/Agora_RTM_SDK_for_Android_v1_2_2.zip' 8 | 9 | - template: cicd/templates/build-ios.yml 10 | parameters: 11 | displayName: 'ChatRoomIos' 12 | workingDirectory: 'iOS' 13 | rtcSdkUrl: 'https://download.agora.io/sdk/release/Agora_Native_SDK_for_iOS_v3_1_0_VOICE.zip?_ga=2.52976808.1493357375.1597372715-269570672.1596526126' 14 | rtmSdkUrl: 'https://download.agora.io/rtmsdk/release/Agora_RTM_SDK_for_iOS_v1_2_2.zip' 15 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/KeyCenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LXH on 2019/12/18. 3 | // Copyright (c) 2019 CavanSu. All rights reserved. 4 | // 5 | 6 | import Foundation 7 | 8 | struct KeyCenter { 9 | static let AppId: String = <#Your App Id#> 10 | 11 | /* assign Token to nil if you have not enabled app certificate 12 | * before you deploy your own token server, you can easily generate a temp token for dev use 13 | * at https://dashboard.agora.io note the token generated are allowed to join corresponding room ONLY. 14 | */ 15 | /* 如果没有打开鉴权Token, 这里的token值给nil就好 16 | * 生成Token需要参照官方文档部署Token服务器,开发阶段若想先不部署服务器, 可以在https://dashbaord.agora.io生成 17 | * 临时Token. 请注意生成Token时指定的频道名, 该Token只允许加入对应的频道 18 | */ 19 | static let Token: String? = <#Temp Access Token#> 20 | 21 | static let RtmToken: String? = <#Temp Rtm Access Token#> 22 | } 23 | -------------------------------------------------------------------------------- /Android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | 19 | android.useAndroidX=true 20 | android.enableJetifier=true 21 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/AttributeKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AttributeKey.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/12/11. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct AttributeKey { 12 | static let KEY_ANCHOR_ID = "anchorId" 13 | static let KEY_SEAT_ARRAY = initSeatKeys() 14 | static let KEY_USER_INFO = "userInfo" 15 | 16 | private static func initSeatKeys() -> [String] { 17 | var strings = [String]() 18 | for i in 0.. Int { 25 | for (i, item) in KEY_SEAT_ARRAY.enumerated() { 26 | if key == item { 27 | return i 28 | } 29 | } 30 | return NSNotFound 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Android/app/src/androidTest/java/io/agora/chatroom/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.InstrumentationRegistry; 6 | import androidx.test.runner.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | 13 | /** 14 | * Instrumentation test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() throws Exception { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getTargetContext(); 24 | 25 | assertEquals("io.agora.chatroom", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/model/AttributeKey.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.model; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.util.Locale; 6 | 7 | public class AttributeKey { 8 | public static final String KEY_ANCHOR_ID = "anchorId"; 9 | public static final String[] KEY_SEAT_ARRAY = initSeatKeys(); 10 | public static final String KEY_USER_INFO = "userInfo"; 11 | 12 | private static String[] initSeatKeys() { 13 | String[] strings = new String[ChannelData.MAX_SEAT_NUM]; 14 | for (int i = 0; i < strings.length; i++) { 15 | strings[i] = String.format(Locale.getDefault(), "seat%d", i); 16 | } 17 | return strings; 18 | } 19 | 20 | public static int indexOfSeatKey(String key) { 21 | for (int i = 0; i < KEY_SEAT_ARRAY.length; i++) { 22 | if (TextUtils.equals(key, KEY_SEAT_ARRAY[i])) return i; 23 | } 24 | return -1; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/VoiceChangerCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceChangerCell.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/27. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class VoiceChangerCell: UICollectionViewCell { 12 | @IBOutlet weak var effect: UIButton! 13 | 14 | func update(_ isEffect: Bool, _ effectSelectedIndex: Int, _ beautifySelectedIndex: Int, _ position: Int) { 15 | var name: String 16 | var selectedIndex: Int 17 | if isEffect { 18 | name = VoiceEffect.allCases[position].description() 19 | selectedIndex = effectSelectedIndex 20 | } else { 21 | name = VoiceBeautify.allCases[position].description() 22 | selectedIndex = beautifySelectedIndex 23 | } 24 | effect.setTitle(name, for: .normal) 25 | effect.borderColor = position == selectedIndex ? #colorLiteral(red: 0, green: 0.4784313725, blue: 1, alpha: 1) : #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0.6) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Android/app/src/main/res/values/strings_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <#Your App Id#> 8 | 9 | 10 | 11 | <#Temp Access Token#> 12 | 13 | 14 | 15 | <#Temp Rtm Access Token#> 16 | 17 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/zh-Hans.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UITextField"; placeholder = "说点什么..."; ObjectID = "5Pp-tn-Ieq"; */ 3 | "5Pp-tn-Ieq.placeholder" = "说点什么..."; 4 | 5 | /* Class = "UINavigationItem"; title = "房间列表"; ObjectID = "Fe3-uR-aXQ"; */ 6 | "Fe3-uR-aXQ.title" = "房间列表"; 7 | 8 | /* Class = "UIButton"; normalTitle = "变声"; ObjectID = "MlU-WI-fjV"; */ 9 | "MlU-WI-fjV.normalTitle" = "变声"; 10 | 11 | /* Class = "UILabel"; text = "向主播赠送了礼物"; ObjectID = "QhM-zl-GUN"; */ 12 | "QhM-zl-GUN.text" = "向主播赠送了礼物"; 13 | 14 | /* Class = "UIButton"; normalTitle = "音效"; ObjectID = "eoi-KU-MBF"; */ 15 | "eoi-KU-MBF.normalTitle" = "音效"; 16 | 17 | /* Class = "UIButton"; normalTitle = "伴奏"; ObjectID = "jMg-Wz-Zfe"; */ 18 | "jMg-Wz-Zfe.normalTitle" = "伴奏"; 19 | 20 | /* Class = "UINavigationItem"; title = "语聊房"; ObjectID = "lqB-1a-WGr"; */ 21 | "lqB-1a-WGr.title" = "语聊房"; 22 | 23 | /* Class = "UIButton"; normalTitle = "0"; ObjectID = "q2g-NQ-eEd"; */ 24 | "q2g-NQ-eEd.normalTitle" = "0"; 25 | 26 | /* Class = "UIButton"; normalTitle = "美声"; ObjectID = "uSM-uv-z2i"; */ 27 | "uSM-uv-z2i.normalTitle" = "美声"; 28 | -------------------------------------------------------------------------------- /Android/popmenu/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/widget/FixedRatioImageView.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import androidx.annotation.Nullable; 7 | import androidx.appcompat.widget.AppCompatImageView; 8 | 9 | public class FixedRatioImageView extends AppCompatImageView { 10 | 11 | private static final float ITEM_BG_RATIO = 141 / (float) 160; 12 | 13 | public FixedRatioImageView(Context context) { 14 | super(context); 15 | } 16 | 17 | public FixedRatioImageView(Context context, @Nullable AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | 21 | public FixedRatioImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 22 | super(context, attrs, defStyleAttr); 23 | } 24 | 25 | @Override 26 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 27 | int width = getMeasuredWidth(); 28 | int height = (int) (width * ITEM_BG_RATIO); 29 | setMeasuredDimension(widthMeasureSpec, height); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Android/popmenu/src/main/java/me/kareluo/ui/OptionItemView.java: -------------------------------------------------------------------------------- 1 | package me.kareluo.ui; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.Gravity; 6 | import android.widget.CheckedTextView; 7 | 8 | /** 9 | * Created by felix on 16/11/21. 10 | */ 11 | public class OptionItemView extends CheckedTextView { 12 | 13 | public OptionItemView(Context context) { 14 | this(context, null, 0); 15 | } 16 | 17 | public OptionItemView(Context context, AttributeSet attrs) { 18 | this(context, attrs, 0); 19 | } 20 | 21 | public OptionItemView(Context context, AttributeSet attrs, int defStyleAttr) { 22 | super(context, attrs, defStyleAttr); 23 | initialize(context, attrs, defStyleAttr); 24 | } 25 | 26 | public OptionItemView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 27 | super(context, attrs, defStyleAttr); 28 | initialize(context, attrs, defStyleAttr); 29 | } 30 | 31 | private void initialize(Context context, AttributeSet attrs, int defStyleAttr) { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/model/Channel.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.model; 2 | 3 | import androidx.annotation.DrawableRes; 4 | 5 | public class Channel { 6 | 7 | private int drawableRes; 8 | private int backgroundRes; 9 | private String name; 10 | 11 | public Channel(@DrawableRes int drawableRes, @DrawableRes int backgroundRes, String name) { 12 | this.drawableRes = drawableRes; 13 | this.backgroundRes = backgroundRes; 14 | this.name = name; 15 | } 16 | 17 | public int getDrawableRes() { 18 | return drawableRes; 19 | } 20 | 21 | public void setDrawableRes(@DrawableRes int drawableRes) { 22 | this.drawableRes = drawableRes; 23 | } 24 | 25 | public int getBackgroundRes() { 26 | return backgroundRes; 27 | } 28 | 29 | public void setBackgroundRes(@DrawableRes int backgroundRes) { 30 | this.backgroundRes = backgroundRes; 31 | } 32 | 33 | public String getName() { 34 | return name; 35 | } 36 | 37 | public void setName(String name) { 38 | this.name = name; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/MessageCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageCell.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/22. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MessageCell: UITableViewCell { 12 | @IBOutlet weak var avatar: UIImageView! 13 | @IBOutlet weak var message: UILabel! 14 | 15 | func update(_ channelData: ChannelData, _ position: Int) { 16 | let message = channelData.getMessageArray()[position] 17 | let userId = message.sendId 18 | 19 | avatar.image = channelData.getMemberAvatar(userId) 20 | 21 | switch message.messageType { 22 | case Message.MESSAGE_TYPE_TEXT: 23 | self.message.isHidden = false 24 | if let member = channelData.getMember(userId), let name = member.name { 25 | self.message.text = "\(name):\(message.content!)" 26 | } else { 27 | self.message.text = "\(userId):\(message.content!)" 28 | } 29 | case Message.MESSAGE_TYPE_IMAGE: 30 | // TODO 31 | break 32 | default: 33 | break 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/layout_item_channel.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Android/popmenu/src/main/java/me/kareluo/ui/PopVerticalScrollView.java: -------------------------------------------------------------------------------- 1 | package me.kareluo.ui; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.widget.ScrollView; 7 | 8 | import me.kareluo.ui.PopLayout.OnBulgeChangeCallback; 9 | 10 | /** 11 | * Created by felix on 17/1/10. 12 | */ 13 | public class PopVerticalScrollView extends ScrollView implements OnBulgeChangeCallback { 14 | 15 | public PopVerticalScrollView(Context context) { 16 | super(context); 17 | } 18 | 19 | public PopVerticalScrollView(Context context, AttributeSet attrs) { 20 | super(context, attrs); 21 | } 22 | 23 | public PopVerticalScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 24 | super(context, attrs, defStyleAttr); 25 | } 26 | 27 | @Override 28 | public void onBulgeChanged(int site, int size) { 29 | if (getChildCount() > 0) { 30 | View view = getChildAt(0); 31 | if (view instanceof OnBulgeChangeCallback) { 32 | ((OnBulgeChangeCallback) view).onBulgeChanged(site, size); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Member.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Member.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/25. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Member: Codable { 12 | var userId: String 13 | var name: String? 14 | var avatarIndex: Int? 15 | 16 | init(userId: String) { 17 | self.userId = userId 18 | } 19 | 20 | init(userId: String, name: String, avatarIndex: Int) { 21 | self.init(userId: userId) 22 | self.name = name 23 | self.avatarIndex = avatarIndex 24 | } 25 | 26 | mutating func update(_ member: Member) { 27 | self.name = member.name 28 | self.avatarIndex = member.avatarIndex 29 | } 30 | 31 | func toJsonString() -> String? { 32 | if let data = try? JSONEncoder().encode(self) { 33 | return String(data: data, encoding: .utf8) 34 | } 35 | return nil 36 | } 37 | 38 | static func fromJsonString(_ str: String) -> Member? { 39 | if let data = str.data(using: .utf8) { 40 | return try? JSONDecoder().decode(Member.self, from: data) 41 | } 42 | return nil 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Android/popmenu/src/main/java/me/kareluo/ui/PopHorizontalScrollView.java: -------------------------------------------------------------------------------- 1 | package me.kareluo.ui; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.widget.HorizontalScrollView; 7 | 8 | import me.kareluo.ui.PopLayout.OnBulgeChangeCallback; 9 | 10 | /** 11 | * Created by felix on 17/1/10. 12 | */ 13 | public class PopHorizontalScrollView extends HorizontalScrollView implements OnBulgeChangeCallback { 14 | 15 | public PopHorizontalScrollView(Context context) { 16 | super(context); 17 | } 18 | 19 | public PopHorizontalScrollView(Context context, AttributeSet attrs) { 20 | super(context, attrs); 21 | } 22 | 23 | public PopHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 24 | super(context, attrs, defStyleAttr); 25 | } 26 | 27 | @Override 28 | public void onBulgeChanged(int site, int size) { 29 | if (getChildCount() > 0) { 30 | View view = getChildAt(0); 31 | if (view instanceof OnBulgeChangeCallback) { 32 | ((OnBulgeChangeCallback) view).onBulgeChanged(site, size); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/CustomView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomView.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/12/16. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | @IBInspectable var cornerRadius: CGFloat { 13 | get { 14 | layer.cornerRadius 15 | } 16 | 17 | set { 18 | layer.cornerRadius = newValue 19 | layer.masksToBounds = newValue > 0 20 | } 21 | } 22 | 23 | @IBInspectable var borderWidth: CGFloat { 24 | get { 25 | layer.borderWidth 26 | } 27 | set { 28 | layer.borderWidth = newValue > 0 ? newValue : 0 29 | } 30 | } 31 | 32 | @IBInspectable var borderColor: UIColor { 33 | get { 34 | UIColor(cgColor: layer.borderColor!) 35 | } 36 | set { 37 | layer.borderColor = newValue.cgColor 38 | } 39 | } 40 | } 41 | 42 | extension UITextField { 43 | @IBInspectable var placeholderColor: UIColor { 44 | get { 45 | value(forKeyPath: "_placeholderLabel.textColor") as! UIColor 46 | } 47 | set { 48 | setValue(newValue, forKeyPath: "_placeholderLabel.textColor") 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/MessageTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageTableViewController.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/22. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import AgoraRtmKit 12 | 13 | class MessageTableViewController: UITableViewController { 14 | private var mChannelData = ChatRoomManager.shared.getChannelData() 15 | 16 | func insertRows(_ position: Int) { 17 | let indexPath = IndexPath(row: position, section: 0) 18 | tableView.insertRows(at: [indexPath], with: .none) 19 | tableView.scrollToRow(at: indexPath, at: .none, animated: true) 20 | } 21 | } 22 | 23 | extension MessageTableViewController { 24 | // MARK: - UITableViewDataSource 25 | 26 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 27 | mChannelData.getMessageArray().count 28 | } 29 | 30 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> MessageCell { 31 | let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as! MessageCell 32 | 33 | cell.selectionStyle = .none 34 | cell.update(mChannelData, indexPath.row) 35 | 36 | return cell 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/activity_channel_grid.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | AgoraChatRoom 4 | 5 | Created by LXH on 2019/12/18. 6 | Copyright © 2019 CavanSu. All rights reserved. 7 | */ 8 | 9 | "Zhao" = "赵"; 10 | "Qian" = "钱"; 11 | "Sun" = "孙"; 12 | "Li" = "李"; 13 | "Zhou" = "周"; 14 | "Wu" = "吴"; 15 | "Zheng" = "郑"; 16 | "Wang" = "王"; 17 | "Feng" = "冯"; 18 | "Chen" = "陈"; 19 | "Chu" = "褚"; 20 | "Wei" = "卫"; 21 | 22 | "Fuqiang" = "富强"; 23 | "Minzhu" = "民主"; 24 | "Wenming" = "文明"; 25 | "Hexie" = "和谐"; 26 | "Ziyou" = "自由"; 27 | "Pingdeng" = "平等"; 28 | "Gongzheng" = "公正"; 29 | "Fazhi" = "法治"; 30 | "Aiguo" = "爱国"; 31 | "Jingye" = "敬业"; 32 | "Chengxin" = "诚信"; 33 | "Youshan" = "友善"; 34 | 35 | "KTV" = "KTV"; 36 | "Live" = "演唱会"; 37 | "Uncle" = "大叔"; 38 | "Girl" = "小姐姐"; 39 | "Studio" = "录音棚"; 40 | "Pop" = "流行"; 41 | "R&B" = "R&B"; 42 | "Phonograph" = "留声机"; 43 | 44 | "Default" = "原声"; 45 | "Thick" = "浑厚"; 46 | "Low" = "低沉"; 47 | "Round" = "圆润"; 48 | "Falsetto" = "假音"; 49 | "Full" = "饱满"; 50 | "Clear" = "清澈"; 51 | "Resounding" = "高亢"; 52 | "Loud" = "嘹亮"; 53 | "OpenAir" = "空旷"; 54 | 55 | "to_broadcast" = "上麦"; 56 | "to_audience" = "下麦"; 57 | "channel_member_list" = "房间成员列表"; 58 | "people" = "人"; 59 | "turn_off_mic" = "禁麦"; 60 | "turn_on_mic" = "解麦"; 61 | "open_seat" = "解封"; 62 | "close_seat" = "封麦"; 63 | "anchor" = "(主播)"; 64 | -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/layout_item_seat.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 20 | 21 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /cicd/templates/build-android.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | displayName: '' 3 | workingDirectory: '' 4 | rtcSdkUrl: '' 5 | rtmSdkUrl: '' 6 | 7 | jobs: 8 | - job: ${{ parameters.displayName }}_Build 9 | displayName: ${{ parameters.displayName }} 10 | 11 | pool: 12 | vmImage: 'macOS-latest' 13 | 14 | variables: 15 | - group: AgoraKeys 16 | 17 | steps: 18 | - script: cd ${{ parameters.workingDirectory }} && ls && python ci.env.py 19 | env: 20 | RTC_SDK_URL: ${{ parameters.rtcSdkUrl }} 21 | RTM_SDK_URL: ${{ parameters.rtmSdkUrl }} 22 | AGORA_APP_ID: $(agora.appId) 23 | 24 | - script: ls ${{ parameters.workingDirectory }}/app/libs 25 | 26 | - task: Gradle@2 27 | inputs: 28 | gradleWrapperFile: ${{ parameters.workingDirectory }}/gradlew 29 | workingDirectory: ${{ parameters.workingDirectory }} 30 | tasks: 'assembleDebug' 31 | publishJUnitResults: false 32 | gradleOptions: '-Xmx3072m' 33 | 34 | - task: CopyFiles@2 35 | inputs: 36 | contents: '**/*.apk' 37 | targetFolder: '$(build.artifactStagingDirectory)' 38 | 39 | - task: PublishBuildArtifacts@1 40 | inputs: 41 | pathtoPublish: '$(build.artifactStagingDirectory)' 42 | artifactName: ${{ parameters.displayName }} -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | AgoraChatRoom 4 | 5 | Created by LXH on 2019/12/18. 6 | Copyright © 2019 CavanSu. All rights reserved. 7 | */ 8 | 9 | "Zhao" = "Zhao"; 10 | "Qian" = "Qian"; 11 | "Sun" = "Sun"; 12 | "Li" = "Li"; 13 | "Zhou" = "Zhou"; 14 | "Wu" = "Wu"; 15 | "Zheng" = "Zheng"; 16 | "Wang" = "Wang"; 17 | "Feng" = "Feng"; 18 | "Chen" = "Chen"; 19 | "Chu" = "Chu"; 20 | "Wei" = "Wei"; 21 | 22 | "Fuqiang" = "Fuqiang"; 23 | "Minzhu" = "Minzhu"; 24 | "Wenming" = "WenMing"; 25 | "Hexie" = "Hexie"; 26 | "Ziyou" = "Ziyou"; 27 | "Pingdeng" = "Pingdeng"; 28 | "Gongzheng" = "Gongzheng"; 29 | "Fazhi" = "Fazhi"; 30 | "Aiguo" = "Aiguo"; 31 | "Jingye" = "Jingye"; 32 | "Chengxin" = "Chengxin"; 33 | "Youshan" = "Youshan"; 34 | 35 | "KTV" = "KTV"; 36 | "Live" = "Live"; 37 | "Uncle" = "Uncle"; 38 | "Girl" = "Girl"; 39 | "Studio" = "Studio"; 40 | "Pop" = "Pop"; 41 | "R&B" = "R&B"; 42 | "Phonograph" = "Phonograph"; 43 | 44 | "Default" = "Default"; 45 | "Thick" = "Thick"; 46 | "Low" = "Low"; 47 | "Round" = "Round"; 48 | "Falsetto" = "Falsetto"; 49 | "Full" = "Full"; 50 | "Clear" = "Clear"; 51 | "Resounding" = "Resounding"; 52 | "Loud" = "Loud"; 53 | "OpenAir" = "OpenAir"; 54 | 55 | "to_broadcast" = "toBroadcast"; 56 | "to_audience" = "toAudience"; 57 | "channel_member_list" = "Channel member list"; 58 | "people" = "people"; 59 | "turn_off_mic" = "Turn off mic"; 60 | "turn_on_mic" = "Turn on mic"; 61 | "open_seat" = "Open"; 62 | "close_seat" = "Close"; 63 | "anchor" = "(Anchor)"; 64 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/AlertUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertUtil.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/26. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct AlertUtil { 12 | static func showAlert(_ root: UIViewController, _ text: String) { 13 | let vc = UIAlertController(title: nil, message: text, preferredStyle: .alert) 14 | vc.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: nil)) 15 | root.present(vc, animated: true, completion: nil) 16 | } 17 | 18 | static func showMenu(_ root: UIView, _ selectors: [Selector]) { 19 | root.becomeFirstResponder() 20 | let menu = UIMenuController.shared 21 | menu.menuItems = [ 22 | UIMenuItem(title: NSLocalizedString("to_broadcast", comment: ""), action: selectors[0]), 23 | UIMenuItem(title: NSLocalizedString("to_audience", comment: ""), action: selectors[1]), 24 | UIMenuItem(title: NSLocalizedString("turn_off_mic", comment: ""), action: selectors[2]), 25 | UIMenuItem(title: NSLocalizedString("turn_on_mic", comment: ""), action: selectors[3]), 26 | UIMenuItem(title: NSLocalizedString("close_seat", comment: ""), action: selectors[4]), 27 | UIMenuItem(title: NSLocalizedString("open_seat", comment: ""), action: selectors[5]) 28 | ] 29 | menu.arrowDirection = .up 30 | menu.setTargetRect(root.frame, in: root.superview!) 31 | menu.setMenuVisible(true, animated: true) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/layout_item_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 24 | 25 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | defaultConfig { 6 | applicationId "io.agora.chatroom" 7 | minSdkVersion 17 8 | targetSdkVersion 29 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | sourceSets { 24 | main { 25 | jniLibs.srcDirs = ['libs'] 26 | } 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | implementation project(':popmenu') 33 | 34 | implementation 'androidx.appcompat:appcompat:1.1.0' 35 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 36 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 37 | implementation "com.google.code.gson:gson:2.8.5" 38 | implementation 'de.hdodenhof:circleimageview:3.0.0' 39 | 40 | implementation 'com.jakewharton:butterknife:10.2.0' 41 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0' 42 | 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 44 | testImplementation 'junit:junit:4.12' 45 | } 46 | -------------------------------------------------------------------------------- /Android/ci.env.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | import re 4 | import os 5 | import shutil 6 | 7 | TARGET_LIBS_ZIP = "agora_sdk.zip" 8 | TARGET_INTERNAL_FOLDER = "agora_sdk" 9 | 10 | 11 | def main(): 12 | RTC_SDK_URL = "" 13 | if "RTC_SDK_URL" in os.environ: 14 | RTC_SDK_URL = os.environ["RTC_SDK_URL"] 15 | 16 | RTM_SDK_URL = "" 17 | if "RTM_SDK_URL" in os.environ: 18 | RTM_SDK_URL = os.environ["RTM_SDK_URL"] 19 | 20 | downloadSDK(RTC_SDK_URL) 21 | downloadSDK(RTM_SDK_URL) 22 | 23 | if not os.path.exists("app/libs"): 24 | os.mkdir("app/libs") 25 | 26 | mv = "cp -f -r " + TARGET_INTERNAL_FOLDER + "/*/libs/* app/libs" 27 | os.system(mv) 28 | 29 | os.remove(TARGET_LIBS_ZIP) 30 | shutil.rmtree(TARGET_INTERNAL_FOLDER, ignore_errors=True) 31 | 32 | appId = "" 33 | if "AGORA_APP_ID" in os.environ: 34 | appId = os.environ["AGORA_APP_ID"] 35 | token = "" 36 | 37 | # if need reset 38 | f = open("./app/src/main/res/values/strings_config.xml", 'r+') 39 | content = f.read() 40 | 41 | # if need reset 42 | contentNew = re.sub(r'<#Your App Id#>', appId, content) 43 | contentNew = re.sub(r'<#Temp Access Token#>', token, contentNew) 44 | contentNew = re.sub(r'<#Temp Rtm Access Token#>', token, contentNew) 45 | 46 | f.seek(0) 47 | f.write(contentNew) 48 | f.truncate() 49 | 50 | 51 | def downloadSDK(url): 52 | wget = "wget " + url + " -O " + TARGET_LIBS_ZIP 53 | os.system(wget) 54 | 55 | unzip = "unzip " + TARGET_LIBS_ZIP + " \"*/libs/*\" -d " + TARGET_INTERNAL_FOLDER 56 | os.system(unzip) 57 | 58 | 59 | if __name__ == "__main__": 60 | main() 61 | -------------------------------------------------------------------------------- /Android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Message.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Message.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/25. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Message: Codable { 12 | static let MESSAGE_TYPE_TEXT = 0 13 | static let MESSAGE_TYPE_IMAGE = 1 14 | static let MESSAGE_TYPE_GIFT = 2 15 | static let MESSAGE_TYPE_ORDER = 3 16 | 17 | static let ORDER_TYPE_AUDIENCE = "toAudience" 18 | static let ORDER_TYPE_BROADCASTER = "toBroadcaster" 19 | static let ORDER_TYPE_MUTE = "mute" 20 | 21 | var messageType: Int = Message.MESSAGE_TYPE_TEXT 22 | var orderType: String? 23 | var content: String? 24 | var sendId: String 25 | 26 | init(content: String?, sendId: UInt) { 27 | self.content = content 28 | self.sendId = String(sendId) 29 | } 30 | 31 | init(messageType: Int, content: String?, sendId: UInt) { 32 | self.init(content: content, sendId: sendId) 33 | self.messageType = messageType 34 | } 35 | 36 | init(orderType: String, content: String?, sendId: UInt) { 37 | self.init(content: content, sendId: sendId) 38 | self.messageType = Message.MESSAGE_TYPE_ORDER 39 | self.orderType = orderType 40 | } 41 | 42 | func toJsonString() -> String? { 43 | if let data = try? JSONEncoder().encode(self) { 44 | return String(data: data, encoding: .utf8) 45 | } 46 | return nil 47 | } 48 | 49 | static func fromJsonString(_ str: String) -> Message? { 50 | if let data = str.data(using: .utf8) { 51 | return try? JSONDecoder().decode(Message.self, from: data) 52 | } 53 | return nil 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/model/Member.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.model; 2 | 3 | import android.text.TextUtils; 4 | 5 | import androidx.annotation.Nullable; 6 | 7 | import com.google.gson.Gson; 8 | 9 | public class Member { 10 | 11 | private String userId; 12 | private String name; 13 | private int avatarIndex; 14 | 15 | public Member(String userId) { 16 | this.userId = userId; 17 | } 18 | 19 | public Member(String userId, String name, int avatarIndex) { 20 | this(userId); 21 | this.name = name; 22 | this.avatarIndex = avatarIndex; 23 | } 24 | 25 | public String getUserId() { 26 | return userId; 27 | } 28 | 29 | public void setUserId(String userId) { 30 | this.userId = userId; 31 | } 32 | 33 | public String getName() { 34 | return name; 35 | } 36 | 37 | public void setName(String name) { 38 | this.name = name; 39 | } 40 | 41 | public int getAvatarIndex() { 42 | return avatarIndex; 43 | } 44 | 45 | public void setAvatarIndex(int avatarIndex) { 46 | this.avatarIndex = avatarIndex; 47 | } 48 | 49 | public void update(Member member) { 50 | this.name = member.name; 51 | this.avatarIndex = member.avatarIndex; 52 | } 53 | 54 | @Override 55 | public boolean equals(@Nullable Object obj) { 56 | if (obj instanceof Member) 57 | return TextUtils.equals(userId, ((Member) obj).userId); 58 | return super.equals(obj); 59 | } 60 | 61 | public String toJsonString() { 62 | return new Gson().toJson(this); 63 | } 64 | 65 | public static Member fromJsonString(String str) { 66 | return new Gson().fromJson(str, Member.class); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/zh-Hans.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | ChatRoom 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSMicrophoneUsageDescription 26 | ChatRoom need mic 27 | UIBackgroundModes 28 | 29 | audio 30 | 31 | UILaunchStoryboardName 32 | LaunchScreen 33 | UIMainStoryboardFile 34 | Main 35 | UIRequiredDeviceCapabilities 36 | 37 | armv7 38 | 39 | UIStatusBarStyle 40 | UIStatusBarStyleLightContent 41 | UISupportedInterfaceOrientations 42 | 43 | UIInterfaceOrientationPortrait 44 | 45 | UISupportedInterfaceOrientations~ipad 46 | 47 | UIInterfaceOrientationPortrait 48 | UIInterfaceOrientationPortraitUpsideDown 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/GiftPopView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GiftPopView.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/12/16. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class GiftPopView: UIView { 12 | @IBOutlet weak var avatar: UIImageView! 13 | @IBOutlet weak var name: UILabel! 14 | 15 | func show(_ userId: String) { 16 | let channelData = ChatRoomManager.shared.getChannelData() 17 | if let member = channelData.getMember(userId) { 18 | name.text = member.name 19 | } else { 20 | name.text = userId 21 | } 22 | avatar.image = channelData.getMemberAvatar(userId) 23 | 24 | isHidden = false 25 | groupAnimation() 26 | perform(#selector(hide), with: nil, afterDelay: 2.5) 27 | } 28 | 29 | func groupAnimation() { 30 | let animation = CAAnimationGroup() 31 | animation.animations = [positionAnimation(), opacityAnimation()] 32 | animation.duration = 1 33 | layer.add(animation, forKey: "groupAnimation") 34 | } 35 | 36 | func positionAnimation() -> CABasicAnimation { 37 | let size = UIApplication.shared.windows[0].bounds.size 38 | let animation = CABasicAnimation.init(keyPath: "position") 39 | animation.fromValue = CGPoint.init(x: size.width / 2, y: size.height / 2 + self.frame.size.height) 40 | animation.toValue = CGPoint.init(x: size.width / 2, y: size.height / 2) 41 | return animation 42 | } 43 | 44 | func opacityAnimation() -> CABasicAnimation { 45 | let animation = CABasicAnimation.init(keyPath: "opacity") 46 | animation.fromValue = NSNumber.init(value: 0) 47 | animation.toValue = NSNumber.init(value: 1) 48 | return animation 49 | } 50 | 51 | @objc private func hide() { 52 | isHidden = true 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /iOS/ci.env.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | import re 4 | import os 5 | import shutil 6 | 7 | TARGET_LIBS_ZIP = "agora_sdk.zip" 8 | TARGET_INTERNAL_FOLDER = "agora_sdk" 9 | 10 | 11 | def main(): 12 | RTC_SDK_URL = "" 13 | if "RTC_SDK_URL" in os.environ: 14 | RTC_SDK_URL = os.environ["RTC_SDK_URL"] 15 | 16 | RTM_SDK_URL = "" 17 | if "RTM_SDK_URL" in os.environ: 18 | RTM_SDK_URL = os.environ["RTM_SDK_URL"] 19 | 20 | SCHEME = "" 21 | if "SCHEME" in os.environ: 22 | SCHEME = os.environ["SCHEME"] 23 | 24 | downloadSDK(RTC_SDK_URL) 25 | downloadSDK(RTM_SDK_URL) 26 | 27 | if not os.path.exists(SCHEME + "/libs"): 28 | os.mkdir(SCHEME + "/libs") 29 | 30 | mv = "cp -f -r " + TARGET_INTERNAL_FOLDER + "/*/libs/* " + SCHEME + "/libs" 31 | os.system(mv) 32 | 33 | os.remove(TARGET_LIBS_ZIP) 34 | shutil.rmtree(TARGET_INTERNAL_FOLDER, ignore_errors=True) 35 | 36 | appId = "" 37 | if "AGORA_APP_ID" in os.environ: 38 | appId = os.environ["AGORA_APP_ID"] 39 | token = "" 40 | 41 | # if need reset 42 | f = open("./" + SCHEME + "/KeyCenter.swift", 'r+') 43 | content = f.read() 44 | 45 | # if need reset 46 | agoraAppString = "\"" + appId + "\"" 47 | agoraTokenString = "\"" + token + "\"" 48 | contentNew = re.sub(r'<#Your App Id#>', agoraAppString, content) 49 | contentNew = re.sub(r'<#Temp Access Token#>', agoraTokenString, contentNew) 50 | contentNew = re.sub(r'<#Temp Rtm Access Token#>', agoraTokenString, contentNew) 51 | 52 | f.seek(0) 53 | f.write(contentNew) 54 | f.truncate() 55 | 56 | 57 | def downloadSDK(url): 58 | wget = "wget " + url + " -O " + TARGET_LIBS_ZIP 59 | os.system(wget) 60 | 61 | unzip = "unzip " + TARGET_LIBS_ZIP + " \"*/libs/*\" -d " + TARGET_INTERNAL_FOLDER 62 | os.system(unzip) 63 | 64 | 65 | if __name__ == "__main__": 66 | main() 67 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/MemberCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemberCell.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/26. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MemberCell: UITableViewCell { 12 | @IBOutlet weak var avatar: UIImageView! 13 | @IBOutlet weak var ivMute: UIImageView! 14 | @IBOutlet weak var name: UILabel! 15 | @IBOutlet weak var role: UIButton! 16 | @IBOutlet weak var btnMute: UIButton! 17 | 18 | func update(_ channelData: ChannelData, _ position: Int) { 19 | let member = channelData.getMemberArray()[position] 20 | let userId = member.userId 21 | 22 | self.avatar.image = channelData.getMemberAvatar(userId) 23 | 24 | if channelData.isUserOnline(userId) { 25 | let muted = channelData.isUserMuted(userId) 26 | self.ivMute.isHidden = false 27 | self.ivMute.image = muted ? #imageLiteral(resourceName: "ic_mic_off_little") : #imageLiteral(resourceName: "ic_mic_on_little") 28 | self.role.setTitle(NSLocalizedString("to_audience", comment: ""), for: .normal) 29 | self.btnMute.isHidden = false 30 | self.btnMute.setTitle(muted ? NSLocalizedString("turn_on_mic", comment: "") : NSLocalizedString("turn_off_mic", comment: ""), for: .normal) 31 | } else { 32 | self.ivMute.isHidden = true 33 | self.role.setTitle(NSLocalizedString("to_broadcast", comment: ""), for: .normal) 34 | self.btnMute.isHidden = true 35 | } 36 | 37 | if !channelData.isAnchorMyself() { 38 | self.role.isHidden = true 39 | self.btnMute.isHidden = true 40 | } 41 | 42 | var name = member.name 43 | if channelData.isAnchor(userId) { 44 | name = "\(name ?? userId)\(NSLocalizedString("anchor", comment: ""))" 45 | } 46 | self.name.text = name 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/dialog_member_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 30 | 31 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/SeatCollectionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SeatCollectionViewController.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/22. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SeatCollectionViewController: UICollectionViewController { 12 | private var mChannelData = ChatRoomManager.shared.getChannelData() 13 | 14 | func reloadItems(_ position: Int) { 15 | collectionView.reloadItems(at: [IndexPath(row: position, section: 0)]) 16 | } 17 | 18 | func reloadItems(_ userId: String?) { 19 | if let `userId` = userId { 20 | let index = mChannelData.indexOfSeatArray(userId) 21 | if index != NSNotFound { 22 | collectionView.reloadItems(at: [IndexPath(row: index, section: 0)]) 23 | } 24 | } 25 | } 26 | } 27 | 28 | extension SeatCollectionViewController { 29 | // MARK: - UICollectionViewDataSource 30 | 31 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 32 | mChannelData.getSeatArray().count 33 | } 34 | 35 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> SeatCell { 36 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SeatCell", for: indexPath) as! SeatCell 37 | 38 | cell.update(mChannelData, indexPath.item) 39 | cell.seat.layoutIfNeeded() 40 | cell.seat.cornerRadius = cell.seat.frame.size.height / 2 41 | 42 | return cell 43 | } 44 | } 45 | 46 | extension SeatCollectionViewController: UICollectionViewDelegateFlowLayout { 47 | // MARK: - UICollectionViewDelegateFlowLayout 48 | 49 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 50 | let width = UIApplication.shared.windows[0].bounds.size.width 51 | let size = Int((width - 10 * 2 * 5) / 5) 52 | return CGSize.init(width: size, height: size) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cicd/templates/build-ios.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | displayName: '' 3 | workingDirectory: '' 4 | rtcSdkUrl: '' 5 | rtmSdkUrl: '' 6 | scheme: 'AgoraChatRoom' 7 | 8 | jobs: 9 | - job: ${{ parameters.displayName }}_Build 10 | displayName: ${{ parameters.displayName }} 11 | 12 | pool: 13 | vmImage: 'macOS-latest' 14 | 15 | variables: 16 | - group: AgoraKeys 17 | - name: configuration 18 | value: 'Release' 19 | - name: sdk 20 | value: 'iphoneos' 21 | 22 | steps: 23 | - script: cd ${{ parameters.workingDirectory }} && ls && python ci.env.py 24 | env: 25 | RTC_SDK_URL: ${{ parameters.rtcSdkUrl }} 26 | RTM_SDK_URL: ${{ parameters.rtmSdkUrl }} 27 | SCHEME: ${{ parameters.scheme }} 28 | AGORA_APP_ID: '$(agora.appId)' 29 | 30 | - script: ls ${{ parameters.workingDirectory }}/${{ parameters.scheme }}/libs 31 | 32 | - task: InstallAppleCertificate@2 33 | inputs: 34 | certSecureFile: 'certificate.p12' 35 | certPwd: '$(agora.password)' 36 | 37 | - task: InstallAppleProvisioningProfile@1 38 | inputs: 39 | provProfileSecureFile: 'AgoraAppsDevProfile.mobileprovision' 40 | 41 | - task: Xcode@5 42 | inputs: 43 | actions: 'build' 44 | configuration: '$(configuration)' 45 | sdk: '$(sdk)' 46 | xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace' 47 | scheme: ${{ parameters.scheme }} 48 | packageApp: 'true' 49 | exportMethod: 'development' 50 | signingOption: 'manual' 51 | signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)' 52 | provisioningProfileUuid: '$(APPLE_PROV_PROFILE_UUID)' 53 | useXcpretty: 'false' 54 | 55 | - task: CopyFiles@2 56 | inputs: 57 | contents: '**/*.ipa' 58 | targetFolder: '$(build.artifactStagingDirectory)' 59 | 60 | - task: PublishBuildArtifacts@1 61 | inputs: 62 | pathtoPublish: '$(build.artifactStagingDirectory)' 63 | artifactName: ${{ parameters.displayName }} -------------------------------------------------------------------------------- /Android/app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 001 5 | 002 6 | 003 7 | 004 8 | 005 9 | 10 | 11 | 12 | @mipmap/img_channel_0 13 | @mipmap/img_channel_1 14 | @mipmap/img_channel_2 15 | @mipmap/img_channel_3 16 | @mipmap/img_channel_4 17 | 18 | 19 | 20 | @mipmap/bg_channel_0 21 | @mipmap/bg_channel_1 22 | @mipmap/bg_channel_2 23 | @mipmap/bg_channel_3 24 | @mipmap/bg_channel_4 25 | 26 | 27 | 28 | @mipmap/img_avatar_0 29 | @mipmap/img_avatar_1 30 | @mipmap/img_avatar_2 31 | @mipmap/img_avatar_3 32 | @mipmap/img_avatar_4 33 | @mipmap/img_avatar_5 34 | @mipmap/img_avatar_6 35 | @mipmap/img_avatar_7 36 | @mipmap/img_avatar_8 37 | @mipmap/img_avatar_9 38 | @mipmap/img_avatar_10 39 | @mipmap/img_avatar_11 40 | 41 | 42 | 43 | 0 44 | 7 45 | 8 46 | 9 47 | 10 48 | 11 49 | 12 50 | 13 51 | 14 52 | 15 53 | 54 | 55 | 56 | 0 57 | 1 58 | 2 59 | 3 60 | 4 61 | 5 62 | 7 63 | 8 64 | 9 65 | 66 | 67 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/model/Message.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.model; 2 | 3 | import com.google.gson.Gson; 4 | 5 | public class Message { 6 | 7 | public static final int MESSAGE_TYPE_TEXT = 0; 8 | public static final int MESSAGE_TYPE_IMAGE = 1; 9 | public static final int MESSAGE_TYPE_GIFT = 2; 10 | public static final int MESSAGE_TYPE_ORDER = 3; 11 | 12 | public static final String ORDER_TYPE_AUDIENCE = "toAudience"; 13 | public static final String ORDER_TYPE_BROADCASTER = "toBroadcaster"; 14 | public static final String ORDER_TYPE_MUTE = "mute"; 15 | 16 | private int messageType = MESSAGE_TYPE_TEXT; 17 | private String orderType; 18 | private String content; 19 | private String sendId; 20 | 21 | public Message(String content, int sendId) { 22 | this.content = content; 23 | this.sendId = String.valueOf(sendId); 24 | } 25 | 26 | public Message(int messageType, String content, int sendId) { 27 | this(content, sendId); 28 | this.messageType = messageType; 29 | } 30 | 31 | public Message(String orderType, String content, int sendId) { 32 | this(content, sendId); 33 | this.messageType = MESSAGE_TYPE_ORDER; 34 | this.orderType = orderType; 35 | } 36 | 37 | public int getMessageType() { 38 | return messageType; 39 | } 40 | 41 | public void setMessageType(int messageType) { 42 | this.messageType = messageType; 43 | } 44 | 45 | public String getOrderType() { 46 | return orderType; 47 | } 48 | 49 | public void setOrderType(String orderType) { 50 | this.orderType = orderType; 51 | } 52 | 53 | public String getContent() { 54 | return content; 55 | } 56 | 57 | public void setContent(String content) { 58 | this.content = content; 59 | } 60 | 61 | public String getSendId() { 62 | return sendId; 63 | } 64 | 65 | public void setSendId(String sendId) { 66 | this.sendId = sendId; 67 | } 68 | 69 | public String toJsonString() { 70 | return new Gson().toJson(this); 71 | } 72 | 73 | public static Message fromJsonString(String str) { 74 | return new Gson().fromJson(str, Message.class); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/dialog_changer_grid.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 27 | 28 | 39 | 40 | 41 | 42 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/util/MemberUtil.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.util; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.content.res.TypedArray; 6 | import android.text.TextUtils; 7 | 8 | import androidx.annotation.ArrayRes; 9 | 10 | import java.util.Random; 11 | import java.util.UUID; 12 | 13 | import io.agora.chatroom.ChatRoomApplication; 14 | import io.agora.chatroom.R; 15 | 16 | public class MemberUtil { 17 | 18 | private static SharedPreferences sp() { 19 | return ChatRoomApplication.instance.getSharedPreferences("member", Context.MODE_PRIVATE); 20 | } 21 | 22 | public static int getUserId() { 23 | int userId = sp().getInt("userId", 0); 24 | if (userId == 0) { 25 | userId = Math.abs(UUID.randomUUID().hashCode()); 26 | sp().edit().putInt("userId", userId).apply(); 27 | } 28 | return userId; 29 | } 30 | 31 | public static String getName() { 32 | String name = sp().getString("name", ""); 33 | if (TextUtils.isEmpty(name)) { 34 | name = String.format("%s %s", randomName(R.array.random_surnames), randomName(R.array.random_names)); 35 | sp().edit().putString("name", name).apply(); 36 | } 37 | return name; 38 | } 39 | 40 | private static String randomName(@ArrayRes int resId) { 41 | String[] names = ChatRoomApplication.instance.getResources().getStringArray(resId); 42 | return names[new Random().nextInt(names.length - 1)]; 43 | } 44 | 45 | public static int getAvatarIndex() { 46 | int avatarIndex = sp().getInt("avatarIndex", 0); 47 | if (avatarIndex == 0) { 48 | TypedArray images = ChatRoomApplication.instance.getResources().obtainTypedArray(R.array.random_avatar_images); 49 | int length = images.length(); 50 | images.recycle(); 51 | avatarIndex = new Random().nextInt(length - 1); 52 | sp().edit().putInt("avatarIndex", avatarIndex).apply(); 53 | } 54 | return avatarIndex; 55 | } 56 | 57 | public static int getAvatarResId(int index) { 58 | TypedArray images = ChatRoomApplication.instance.getResources().obtainTypedArray(R.array.random_avatar_images); 59 | int resId = images.getResourceId(index, R.mipmap.ic_unkown); 60 | images.recycle(); 61 | return resId; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/activity/ChannelGridActivity.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.activity; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Rect; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.recyclerview.widget.GridLayoutManager; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import butterknife.BindView; 14 | import butterknife.ButterKnife; 15 | import io.agora.chatroom.R; 16 | import io.agora.chatroom.adapter.ChannelGridAdapter; 17 | import io.agora.chatroom.model.Channel; 18 | 19 | public class ChannelGridActivity extends AppCompatActivity implements ChannelGridAdapter.OnItemClickListener { 20 | 21 | @BindView(R.id.rv_channel_grid) 22 | RecyclerView rv_channel_grid; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_channel_grid); 28 | ButterKnife.bind(this); 29 | 30 | initRecyclerView(); 31 | } 32 | 33 | private void initRecyclerView() { 34 | rv_channel_grid.setHasFixedSize(true); 35 | 36 | ChannelGridAdapter adapter = new ChannelGridAdapter(this); 37 | adapter.setOnItemClickListener(this); 38 | rv_channel_grid.setAdapter(adapter); 39 | 40 | rv_channel_grid.setLayoutManager(new GridLayoutManager(this, 2)); 41 | 42 | int spacing = getResources().getDimensionPixelSize(R.dimen.item_channel_spacing); 43 | rv_channel_grid.addItemDecoration(new RecyclerView.ItemDecoration() { 44 | @Override 45 | public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { 46 | super.getItemOffsets(outRect, view, parent, state); 47 | outRect.set(spacing, spacing, spacing, spacing); 48 | } 49 | }); 50 | } 51 | 52 | @Override 53 | public void onItemClick(View view, int position, Channel channel) { 54 | Intent intent = new Intent(ChannelGridActivity.this, ChatRoomActivity.class); 55 | intent.putExtra(ChatRoomActivity.BUNDLE_KEY_CHANNEL_ID, channel.getName()); 56 | intent.putExtra(ChatRoomActivity.BUNDLE_KEY_BACKGROUND_RES, channel.getBackgroundRes()); 57 | startActivity(intent); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Android/app/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 语聊房 4 | 语聊房 5 | 房间列表 6 | 上麦 7 | 下麦 8 | 变声 9 | 伴奏 10 | 0 11 | 房间成员列表(%1$d人) 12 | 禁麦 13 | 解麦 14 | 封麦 15 | 解封 16 | 向主播赠送了礼物 17 | 说点什么… 18 | 音效 19 | 美声 20 | (主播) 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 富强 39 | 民主 40 | 文明 41 | 和谐 42 | 自由 43 | 平等 44 | 公正 45 | 法治 46 | 爱国 47 | 敬业 48 | 诚信 49 | 友善 50 | 51 | 52 | 53 | 原声 54 | KTV 55 | 演唱会 56 | 大叔 57 | 小姐姐 58 | 录音棚 59 | 流行 60 | R&B 61 | 留声机 62 | 63 | 64 | 65 | 原声 66 | 浑厚 67 | 低沉 68 | 圆润 69 | 假音 70 | 饱满 71 | 清澈 72 | 高亢 73 | 嘹亮 74 | 空旷 75 | 76 | 77 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/widget/GiftPopView.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.animation.AlphaAnimation; 8 | import android.view.animation.AnimationSet; 9 | import android.view.animation.TranslateAnimation; 10 | import android.widget.FrameLayout; 11 | import android.widget.ImageView; 12 | import android.widget.TextView; 13 | 14 | import androidx.annotation.Nullable; 15 | 16 | import butterknife.BindView; 17 | import butterknife.ButterKnife; 18 | import io.agora.chatroom.R; 19 | import io.agora.chatroom.manager.ChatRoomManager; 20 | import io.agora.chatroom.model.ChannelData; 21 | import io.agora.chatroom.model.Member; 22 | 23 | public class GiftPopView extends FrameLayout { 24 | 25 | @BindView(R.id.iv_avatar) 26 | ImageView iv_avatar; 27 | @BindView(R.id.tv_name) 28 | TextView tv_name; 29 | 30 | public GiftPopView(Context context) { 31 | super(context); 32 | init(context); 33 | } 34 | 35 | public GiftPopView(Context context, @Nullable AttributeSet attrs) { 36 | super(context, attrs); 37 | init(context); 38 | } 39 | 40 | public GiftPopView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 41 | super(context, attrs, defStyleAttr); 42 | init(context); 43 | } 44 | 45 | private void init(Context context) { 46 | LayoutInflater.from(context).inflate(R.layout.layout_gift, this); 47 | ButterKnife.bind(this); 48 | } 49 | 50 | public void show(String userId) { 51 | ChannelData channelData = ChatRoomManager.instance(getContext()).getChannelData(); 52 | Member member = channelData.getMember(userId); 53 | if (member != null) 54 | tv_name.setText(member.getName()); 55 | else 56 | tv_name.setText(userId); 57 | iv_avatar.setImageResource(channelData.getMemberAvatar(userId)); 58 | 59 | setVisibility(View.VISIBLE); 60 | animation(); 61 | postDelayed(() -> setVisibility(View.GONE), 2500); 62 | } 63 | 64 | private void animation() { 65 | AnimationSet animationSet = new AnimationSet(true); 66 | measure(0, 0); 67 | animationSet.addAnimation(new TranslateAnimation(0, 0, getMeasuredHeight(), 0)); 68 | animationSet.addAnimation(new AlphaAnimation(0, 1)); 69 | animationSet.setDuration(1000); 70 | startAnimation(animationSet); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/VoiceChanger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceChanger.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/12/18. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum VoiceEffect: Int, CaseIterable { 12 | case DEFAULT = 0 13 | case KTV 14 | case LIVE 15 | case UNCLE 16 | case GIRL 17 | case STUDIO 18 | case POP = 7 19 | case RNB 20 | case PHONOGRAPH 21 | 22 | func description() -> String { 23 | switch self { 24 | case .DEFAULT: 25 | return NSLocalizedString("Default", comment: "") 26 | case .KTV: 27 | return NSLocalizedString("KTV", comment: "") 28 | case .LIVE: 29 | return NSLocalizedString("Live", comment: "") 30 | case .UNCLE: 31 | return NSLocalizedString("Uncle", comment: "") 32 | case .GIRL: 33 | return NSLocalizedString("Girl", comment: "") 34 | case .STUDIO: 35 | return NSLocalizedString("Studio", comment: "") 36 | case .POP: 37 | return NSLocalizedString("Pop", comment: "") 38 | case .RNB: 39 | return NSLocalizedString("R&B", comment: "") 40 | case .PHONOGRAPH: 41 | return NSLocalizedString("Phonograph", comment: "") 42 | } 43 | } 44 | } 45 | 46 | enum VoiceBeautify: Int, CaseIterable { 47 | case DEFAULT = 0 48 | case THICK = 7 49 | case LOW 50 | case ROUND 51 | case FALSETTO 52 | case FULL 53 | case CLEAR 54 | case RESOUNDING 55 | case LOUD 56 | case OPENAIR 57 | 58 | func description() -> String { 59 | switch self { 60 | case .DEFAULT: 61 | return NSLocalizedString("Default", comment: "") 62 | case .THICK: 63 | return NSLocalizedString("Thick", comment: "") 64 | case .LOW: 65 | return NSLocalizedString("Low", comment: "") 66 | case .ROUND: 67 | return NSLocalizedString("Round", comment: "") 68 | case .FALSETTO: 69 | return NSLocalizedString("Falsetto", comment: "") 70 | case .FULL: 71 | return NSLocalizedString("Full", comment: "") 72 | case .CLEAR: 73 | return NSLocalizedString("Clear", comment: "") 74 | case .RESOUNDING: 75 | return NSLocalizedString("Resounding", comment: "") 76 | case .LOUD: 77 | return NSLocalizedString("Loud", comment: "") 78 | case .OPENAIR: 79 | return NSLocalizedString("OpenAir", comment: "") 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/layout_gift.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 20 | 21 | 29 | 30 | 36 | 37 | 44 | 45 | 46 | 47 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/ChannelViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelCollectionViewController.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/12/5. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AgoraRtmKit 11 | 12 | class ChannelViewController: UIViewController { 13 | let mChannelArray: [Channel] = [ 14 | Channel(drawableRes: #imageLiteral(resourceName: "img_channel_0"), backgroundRes: #imageLiteral(resourceName: "bg_channel_0"), name: "001"), 15 | Channel(drawableRes: #imageLiteral(resourceName: "img_channel_1"), backgroundRes: #imageLiteral(resourceName: "bg_channel_1"), name: "002"), 16 | Channel(drawableRes: #imageLiteral(resourceName: "img_channel_2"), backgroundRes: #imageLiteral(resourceName: "bg_channel_2"), name: "003"), 17 | Channel(drawableRes: #imageLiteral(resourceName: "img_channel_3"), backgroundRes: #imageLiteral(resourceName: "bg_channel_3"), name: "004"), 18 | Channel(drawableRes: #imageLiteral(resourceName: "img_channel_4"), backgroundRes: #imageLiteral(resourceName: "bg_channel_4"), name: "005"), 19 | ] 20 | 21 | override var preferredStatusBarStyle: UIStatusBarStyle { 22 | .lightContent 23 | } 24 | 25 | // MARK: - Navigation 26 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 27 | switch segue.identifier { 28 | case "chatRoom": 29 | let channel = sender as! Channel 30 | let vc = segue.destination as! ChatRoomViewController 31 | vc.bgImg = channel.backgroundRes 32 | vc.title = channel.name 33 | break; 34 | default: 35 | break; 36 | } 37 | } 38 | } 39 | 40 | extension ChannelViewController: UICollectionViewDataSource { 41 | // MARK: - UICollectionViewDataSource 42 | 43 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 44 | mChannelArray.count 45 | } 46 | 47 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 48 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ChannelCell", for: indexPath) as! ChannelCell 49 | 50 | cell.update(mChannelArray[indexPath.row]) 51 | 52 | return cell 53 | } 54 | } 55 | 56 | extension ChannelViewController: UICollectionViewDelegate { 57 | // MARK: - UICollectionViewDelegate 58 | 59 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 60 | self.performSegue(withIdentifier: "chatRoom", sender: mChannelArray[indexPath.row]) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /Android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ChatRoom 3 | ChatRoom 4 | ChannelList 5 | toBroadcast 6 | toAudience 7 | Mixing 8 | 0 9 | Channel member list(%1$d people) 10 | Turn off mic 11 | Turn on mic 12 | Open 13 | Give gift to anchor 14 | Say something… 15 | Voice Effect 16 | (Anchor) 17 | Changer 18 | Voice Beautify 19 | Close 20 | 21 | 22 | Zhao 23 | Qian 24 | Sun 25 | Li 26 | Zhou 27 | Wu 28 | Zheng 29 | Wang 30 | Feng 31 | Chen 32 | Chu 33 | Wei 34 | 35 | 36 | 37 | Fuqiang 38 | Minzhu 39 | Wenming 40 | Hexie 41 | Ziyou 42 | Pingdeng 43 | Gongzheng 44 | Fazhi 45 | Aiguo 46 | Jingye 47 | Chengxin 48 | Youshan 49 | 50 | 51 | 52 | Default 53 | KTV 54 | Live 55 | Uncle 56 | Girl 57 | Studio 58 | Pop 59 | R&B 60 | Phonograph 61 | 62 | 63 | 64 | Default 65 | Thick 66 | Low 67 | Round 68 | Falsetto 69 | Full 70 | Clear 71 | Resounding 72 | Loud 73 | OpenAir 74 | 75 | 76 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/MemberViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemberViewController.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/26. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MemberViewController: UIViewController { 12 | @IBOutlet weak var num: UILabel! 13 | @IBOutlet weak var listMember: UITableView! 14 | 15 | private var mChannelData = ChatRoomManager.shared.getChannelData() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | refreshTitle() 20 | } 21 | 22 | func refreshTitle() { 23 | num.text = "\(NSLocalizedString("channel_member_list", comment: ""))(\(mChannelData.getMemberArray().count)\(NSLocalizedString("people", comment: "")))" 24 | } 25 | 26 | func reloadData() { 27 | refreshTitle() 28 | listMember.reloadData() 29 | } 30 | 31 | func reloadRowsByUserId(_ userId: String) { 32 | let index = mChannelData.indexOfMemberArray(userId) 33 | if index != NSNotFound { 34 | listMember.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none) 35 | } 36 | } 37 | 38 | @IBAction func onClick(_ sender: UIButton) { 39 | self.dismiss(animated: true, completion: nil) 40 | } 41 | } 42 | 43 | extension MemberViewController: UITableViewDataSource { 44 | // MARK: - UITableViewDataSource 45 | 46 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 47 | mChannelData.getMemberArray().count 48 | } 49 | 50 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 51 | let cell = tableView.dequeueReusableCell(withIdentifier: "MemberCell", for: indexPath) as! MemberCell 52 | 53 | cell.selectionStyle = .none 54 | cell.update(mChannelData, indexPath.row) 55 | cell.role.tag = indexPath.row 56 | cell.role.addTarget(self, action: #selector(role(_:)), for: .touchUpInside) 57 | cell.btnMute.tag = indexPath.row 58 | cell.btnMute.addTarget(self, action: #selector(mute(_:)), for: .touchUpInside) 59 | 60 | return cell 61 | } 62 | 63 | @objc private func role(_ sender: UIButton) { 64 | let userId = mChannelData.getMemberArray()[sender.tag].userId 65 | if mChannelData.isUserOnline(userId) { 66 | ChatRoomManager.shared.toAudience(userId, nil) 67 | } else { 68 | ChatRoomManager.shared.toBroadcaster(userId, mChannelData.firstIndexOfEmptySeat()) 69 | } 70 | } 71 | 72 | @objc private func mute(_ sender: UIButton) { 73 | let userId = mChannelData.getMemberArray()[sender.tag].userId 74 | ChatRoomManager.shared.muteMic(userId, !mChannelData.isUserMuted(userId)) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/widget/VoiceChangerGridRecyclerView.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Rect; 5 | import android.util.AttributeSet; 6 | import android.view.View; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.annotation.Nullable; 10 | import androidx.recyclerview.widget.GridLayoutManager; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import io.agora.chatroom.R; 14 | import io.agora.chatroom.adapter.VoiceChangerGridAdapter; 15 | 16 | public class VoiceChangerGridRecyclerView extends RecyclerView { 17 | 18 | private VoiceChangerGridAdapter mEffectAdapter; 19 | private VoiceChangerGridAdapter mBeautifyAdapter; 20 | 21 | public VoiceChangerGridRecyclerView(@NonNull Context context) { 22 | super(context); 23 | init(context); 24 | } 25 | 26 | public VoiceChangerGridRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { 27 | super(context, attrs); 28 | init(context); 29 | } 30 | 31 | public VoiceChangerGridRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 32 | super(context, attrs, defStyleAttr); 33 | init(context); 34 | } 35 | 36 | private void init(Context context) { 37 | setHasFixedSize(true); 38 | 39 | mEffectAdapter = new VoiceChangerGridAdapter(context, R.array.voice_effect_keys, R.array.voice_effect_values); 40 | mBeautifyAdapter = new VoiceChangerGridAdapter(context, R.array.voice_beautify_keys, R.array.voice_beautify_values); 41 | 42 | setLayoutManager(new GridLayoutManager(context, 3)); 43 | 44 | int spacing = getResources().getDimensionPixelSize(R.dimen.dialog_member_item_spacing); 45 | addItemDecoration(new RecyclerView.ItemDecoration() { 46 | @Override 47 | public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { 48 | super.getItemOffsets(outRect, view, parent, state); 49 | outRect.set(spacing / 2, spacing, spacing / 2, 0); 50 | } 51 | }); 52 | } 53 | 54 | public void showEffect(int selectedIndex) { 55 | setAdapter(mEffectAdapter); 56 | mEffectAdapter.setSelectedIndex(selectedIndex); 57 | } 58 | 59 | public void showBeautify(int selectedIndex) { 60 | setAdapter(mBeautifyAdapter); 61 | mBeautifyAdapter.setSelectedIndex(selectedIndex); 62 | } 63 | 64 | public void setOnItemClickListener(VoiceChangerGridAdapter.OnItemClickListener listener) { 65 | mEffectAdapter.setOnItemClickListener(listener); 66 | mBeautifyAdapter.setOnItemClickListener(listener); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/adapter/MessageListAdapter.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import butterknife.BindView; 14 | import butterknife.ButterKnife; 15 | import io.agora.chatroom.R; 16 | import io.agora.chatroom.model.ChannelData; 17 | import io.agora.chatroom.model.Member; 18 | import io.agora.chatroom.model.Message; 19 | import io.agora.chatroom.manager.ChatRoomManager; 20 | 21 | public class MessageListAdapter extends RecyclerView.Adapter { 22 | 23 | private LayoutInflater mInflater; 24 | 25 | private ChannelData mChannelData; 26 | 27 | public MessageListAdapter(Context context) { 28 | mInflater = LayoutInflater.from(context); 29 | mChannelData = ChatRoomManager.instance(context).getChannelData(); 30 | } 31 | 32 | @NonNull 33 | @Override 34 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 35 | View view = mInflater.inflate(R.layout.layout_item_message, parent, false); 36 | return new ViewHolder(view); 37 | } 38 | 39 | @Override 40 | public int getItemCount() { 41 | if (mChannelData == null) return 0; 42 | return mChannelData.getMessageList().size(); 43 | } 44 | 45 | @Override 46 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 47 | Message message = mChannelData.getMessageList().get(position); 48 | String userId = message.getSendId(); 49 | 50 | holder.iv_avatar.setImageResource(mChannelData.getMemberAvatar(userId)); 51 | 52 | switch (message.getMessageType()) { 53 | case Message.MESSAGE_TYPE_TEXT: 54 | holder.tv_message.setVisibility(View.VISIBLE); 55 | Member member = mChannelData.getMember(userId); 56 | holder.tv_message.setText(String.format("%s:%s", member != null ? member.getName() : userId, message.getContent())); 57 | break; 58 | case Message.MESSAGE_TYPE_IMAGE: 59 | // TODO 60 | break; 61 | } 62 | } 63 | 64 | class ViewHolder extends RecyclerView.ViewHolder { 65 | @BindView(R.id.iv_avatar) 66 | ImageView iv_avatar; 67 | @BindView(R.id.tv_message) 68 | TextView tv_message; 69 | @BindView(R.id.iv_image) 70 | ImageView iv_image; 71 | 72 | ViewHolder(View view) { 73 | super(view); 74 | ButterKnife.bind(this, view); 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/adapter/VoiceChangerGridAdapter.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import androidx.annotation.ArrayRes; 10 | import androidx.annotation.NonNull; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import butterknife.BindView; 14 | import butterknife.ButterKnife; 15 | import io.agora.chatroom.R; 16 | 17 | public class VoiceChangerGridAdapter extends RecyclerView.Adapter { 18 | 19 | private LayoutInflater mInflater; 20 | private VoiceChangerGridAdapter.OnItemClickListener mListener; 21 | private int mSelectedIndex; 22 | 23 | private String[] mKeys; 24 | private int[] mValues; 25 | 26 | public VoiceChangerGridAdapter(Context context, @ArrayRes int keysId, @ArrayRes int valuesId) { 27 | mInflater = LayoutInflater.from(context); 28 | mKeys = context.getResources().getStringArray(keysId); 29 | mValues = context.getResources().getIntArray(valuesId); 30 | } 31 | 32 | @NonNull 33 | @Override 34 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 35 | View view = mInflater.inflate(R.layout.layout_item_changer, parent, false); 36 | return new ViewHolder(view); 37 | } 38 | 39 | @Override 40 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 41 | String key = mKeys[position]; 42 | 43 | holder.tv_changer.setText(key); 44 | holder.tv_changer.setBackgroundResource(mSelectedIndex == position ? R.drawable.bg_changer_item_selected : R.drawable.bg_changer_item_normal); 45 | 46 | holder.tv_changer.setOnClickListener((view) -> { 47 | setSelectedIndex(position); 48 | if (mListener != null) 49 | mListener.onItemClick(position, mValues[position]); 50 | }); 51 | } 52 | 53 | @Override 54 | public int getItemCount() { 55 | if (mKeys == null) return 0; 56 | return mKeys.length; 57 | } 58 | 59 | public void setSelectedIndex(int index) { 60 | notifyItemChanged(mSelectedIndex); 61 | mSelectedIndex = index; 62 | notifyItemChanged(index); 63 | } 64 | 65 | public void setOnItemClickListener(VoiceChangerGridAdapter.OnItemClickListener listener) { 66 | mListener = listener; 67 | } 68 | 69 | class ViewHolder extends RecyclerView.ViewHolder { 70 | @BindView(R.id.tv_changer) 71 | TextView tv_changer; 72 | 73 | ViewHolder(View view) { 74 | super(view); 75 | ButterKnife.bind(this, view); 76 | } 77 | } 78 | 79 | public interface OnItemClickListener { 80 | void onItemClick(int position, int value); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/util/AlertUtil.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.util; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.os.Handler; 6 | import android.os.Looper; 7 | import android.view.View; 8 | import android.widget.PopupWindow; 9 | import android.widget.Toast; 10 | 11 | import androidx.appcompat.app.AlertDialog; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import io.agora.chatroom.ChatRoomApplication; 17 | import io.agora.chatroom.R; 18 | import me.kareluo.ui.OptionMenu; 19 | import me.kareluo.ui.OptionMenuView; 20 | import me.kareluo.ui.PopupMenuView; 21 | import me.kareluo.ui.PopupView; 22 | 23 | public class AlertUtil { 24 | 25 | public static void showToast(String format, Object... args) { 26 | new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(ChatRoomApplication.instance, String.format(format, args), Toast.LENGTH_SHORT).show()); 27 | } 28 | 29 | public static void showPop(Context context, View view, int[] ids, OptionMenuView.OnOptionMenuClickListener clickListener, PopupWindow.OnDismissListener dismissListener) { 30 | PopupMenuView popupMenuView = new PopupMenuView(context); 31 | List menus = new ArrayList<>(); 32 | for (int id : ids) { 33 | int strId = 0; 34 | switch (id) { 35 | case R.id.to_audience: 36 | strId = R.string.to_audience; 37 | break; 38 | case R.id.to_broadcast: 39 | strId = R.string.to_broadcast; 40 | break; 41 | case R.id.turn_off_mic: 42 | strId = R.string.turn_off_mic; 43 | break; 44 | case R.id.turn_on_mic: 45 | strId = R.string.turn_on_mic; 46 | break; 47 | case R.id.close_seat: 48 | strId = R.string.close_seat; 49 | break; 50 | case R.id.open_seat: 51 | strId = R.string.open_seat; 52 | break; 53 | } 54 | menus.add(new OptionMenu(id, strId)); 55 | } 56 | popupMenuView.setMenuItems(menus); 57 | popupMenuView.setSites(PopupView.SITE_BOTTOM, PopupView.SITE_LEFT, PopupView.SITE_TOP, PopupView.SITE_RIGHT); 58 | popupMenuView.setOnMenuClickListener(clickListener); 59 | popupMenuView.setOnDismissListener(dismissListener); 60 | popupMenuView.show(view); 61 | } 62 | 63 | public static void showAlertDialog(Context context, String text, String action, DialogInterface.OnClickListener listener) { 64 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 65 | builder.setTitle(text); 66 | builder.setPositiveButton(action, listener); 67 | builder.create().show(); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/layout_item_member.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 19 | 20 | 33 | 34 | 46 | 47 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/MemberUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemberUtil.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/12/5. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct MemberUtil { 12 | static let surNames = [NSLocalizedString("Zhao", comment: ""), NSLocalizedString("Qian", comment: ""), NSLocalizedString("Sun", comment: ""), NSLocalizedString("Li", comment: ""), NSLocalizedString("Zhou", comment: ""), NSLocalizedString("Wu", comment: ""), NSLocalizedString("Zheng", comment: ""), NSLocalizedString("Wang", comment: ""), NSLocalizedString("Feng", comment: ""), NSLocalizedString("Chen", comment: ""), NSLocalizedString("Chu", comment: ""), NSLocalizedString("Wei", comment: "")] 13 | static let names = [NSLocalizedString("Fuqiang", comment: ""), NSLocalizedString("Minzhu", comment: ""), NSLocalizedString("Wenming", comment: ""), NSLocalizedString("Hexie", comment: ""), NSLocalizedString("Ziyou", comment: ""), NSLocalizedString("Pingdeng", comment: ""), NSLocalizedString("Gongzheng", comment: ""), NSLocalizedString("Fazhi", comment: ""), NSLocalizedString("Aiguo", comment: ""), NSLocalizedString("Jingye", comment: ""), NSLocalizedString("Chengxin", comment: ""), NSLocalizedString("Youshan", comment: "")] 14 | static let avatarImages: [UIImage] = [#imageLiteral(resourceName: "img_header_0"), #imageLiteral(resourceName: "img_header_1"), #imageLiteral(resourceName: "img_header_2"), #imageLiteral(resourceName: "img_header_3"), #imageLiteral(resourceName: "img_header_4"), #imageLiteral(resourceName: "img_header_5"), #imageLiteral(resourceName: "img_header_6"), #imageLiteral(resourceName: "img_header_7"), #imageLiteral(resourceName: "img_header_8"), #imageLiteral(resourceName: "img_header_9"), #imageLiteral(resourceName: "img_header_10"), #imageLiteral(resourceName: "img_header_11")] 15 | 16 | static func getUserId() -> Int32 { 17 | var userId = Int32(truncatingIfNeeded: UserDefaults.standard.integer(forKey: "userId")) 18 | if userId == 0 { 19 | userId = abs(Int32(truncatingIfNeeded: UUID().uuidString.hashValue)) 20 | UserDefaults.standard.set(userId, forKey: "userId") 21 | UserDefaults.standard.synchronize() 22 | } 23 | return userId 24 | } 25 | 26 | static func getName() -> String { 27 | guard let name = UserDefaults.standard.string(forKey: "name") else { 28 | let name = "\(randomName(surNames)) \(randomName(names))" 29 | UserDefaults.standard.set(name, forKey: "name") 30 | UserDefaults.standard.synchronize() 31 | return name 32 | } 33 | return name 34 | } 35 | 36 | private static func randomName(_ strArray: [String]) -> String { 37 | strArray[Int.random(in: 0.. Int { 41 | var avatarIndex = UserDefaults.standard.integer(forKey: "avatarIndex") 42 | if avatarIndex == 0 { 43 | avatarIndex = Int.random(in: 0.. UIImage { 51 | avatarImages[index] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/adapter/ChannelGridAdapter.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.content.res.TypedArray; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.recyclerview.widget.RecyclerView; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import butterknife.BindView; 19 | import butterknife.ButterKnife; 20 | import io.agora.chatroom.R; 21 | import io.agora.chatroom.model.Channel; 22 | 23 | public class ChannelGridAdapter extends RecyclerView.Adapter { 24 | 25 | private LayoutInflater mInflater; 26 | private OnItemClickListener mListener; 27 | 28 | private List mChannelList; 29 | 30 | public ChannelGridAdapter(Context context) { 31 | mInflater = LayoutInflater.from(context); 32 | initData(context); 33 | } 34 | 35 | private void initData(Context context) { 36 | mChannelList = new ArrayList<>(); 37 | 38 | Resources resources = context.getResources(); 39 | TypedArray drawables = resources.obtainTypedArray(R.array.channel_list_drawable); 40 | TypedArray backgrounds = resources.obtainTypedArray(R.array.channel_list_background); 41 | String[] titles = resources.getStringArray(R.array.channel_list_title); 42 | 43 | for (int i = 0; i < titles.length; i++) { 44 | mChannelList.add(new Channel( 45 | drawables.getResourceId(i, 0), 46 | backgrounds.getResourceId(i, 0), 47 | titles[i] 48 | )); 49 | } 50 | 51 | drawables.recycle(); 52 | backgrounds.recycle(); 53 | } 54 | 55 | @NonNull 56 | @Override 57 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 58 | View view = mInflater.inflate(R.layout.layout_item_channel, parent, false); 59 | return new ViewHolder(view); 60 | } 61 | 62 | @Override 63 | public int getItemCount() { 64 | if (mChannelList == null) return 0; 65 | return mChannelList.size(); 66 | } 67 | 68 | @Override 69 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 70 | Channel channel = mChannelList.get(position); 71 | 72 | holder.iv_image.setImageResource(channel.getDrawableRes()); 73 | holder.tv_name.setText(channel.getName()); 74 | 75 | holder.view.setOnClickListener((view) -> { 76 | if (mListener != null) 77 | mListener.onItemClick(view, position, channel); 78 | }); 79 | } 80 | 81 | public void setOnItemClickListener(OnItemClickListener listener) { 82 | mListener = listener; 83 | } 84 | 85 | class ViewHolder extends RecyclerView.ViewHolder { 86 | View view; 87 | @BindView(R.id.iv_image) 88 | ImageView iv_image; 89 | @BindView(R.id.tv_name) 90 | TextView tv_name; 91 | 92 | ViewHolder(View view) { 93 | super(view); 94 | this.view = view; 95 | ButterKnife.bind(this, view); 96 | } 97 | } 98 | 99 | public interface OnItemClickListener { 100 | void onItemClick(View view, int position, Channel channel); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /Android/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 17sp 3 | 15dp 4 | 14dp 5 | 6dp 6 | 10dp 7 | 0.5dp 8 | 1dp 9 | 19dp 10 | 11 | 12 | 10dp 13 | 15dp 14 | 12dp 15 | 14sp 16 | 17 | 18 | 30dp 19 | 62dp 20 | 34dp 21 | 17sp 22 | 25dp 23 | 24 | 10dp 25 | 60dp 26 | 50dp 27 | 25dp 28 | 29 | 15dp 30 | 30dp 31 | 8dp 32 | 15dp 33 | 9dp 34 | 14sp 35 | 36 | 15dp 37 | 12dp 38 | 14sp 39 | 9dp 40 | 38dp 41 | 17sp 42 | 43 | 220dp 44 | 56dp 45 | 28dp 46 | 4dp 47 | 11dp 48 | 48dp 49 | 14sp 50 | 12sp 51 | 52 | 350dp 53 | 15dp 54 | 55dp 55 | 28dp 56 | 17sp 57 | 100dp 58 | 44dp 59 | 22dp 60 | 61 | 62 | 325dp 63 | 375dp 64 | 16dp 65 | 18dp 66 | 14sp 67 | 26dp 68 | 14sp 69 | 19dp 70 | 71 | 19dp 72 | 36dp 73 | 46dp 74 | 32dp 75 | 12sp 76 | 12dp 77 | 78 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/widget/VoiceChangerDialog.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.widget; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.view.Gravity; 7 | import android.view.Window; 8 | import android.view.WindowManager; 9 | import android.widget.RadioGroup; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.annotation.Nullable; 13 | import androidx.fragment.app.DialogFragment; 14 | 15 | import butterknife.BindView; 16 | import butterknife.ButterKnife; 17 | import io.agora.chatroom.R; 18 | import io.agora.chatroom.adapter.VoiceChangerGridAdapter; 19 | import io.agora.chatroom.manager.ChatRoomManager; 20 | import io.agora.chatroom.manager.RtcManager; 21 | 22 | public class VoiceChangerDialog extends DialogFragment implements VoiceChangerGridAdapter.OnItemClickListener, RadioGroup.OnCheckedChangeListener { 23 | 24 | @BindView(R.id.rg_title) 25 | RadioGroup rg_title; 26 | @BindView(R.id.rv_changer_grid) 27 | VoiceChangerGridRecyclerView rv_changer_grid; 28 | 29 | private Context mContext; 30 | 31 | private int mEffectSelectedIndex; 32 | private int mBeautifySelectedIndex; 33 | 34 | @Override 35 | public void onAttach(@NonNull Context context) { 36 | super.onAttach(context); 37 | mContext = context; 38 | } 39 | 40 | @NonNull 41 | @Override 42 | public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { 43 | Dialog dialog = new Dialog(mContext); 44 | dialog.setContentView(R.layout.dialog_changer_grid); 45 | 46 | ButterKnife.bind(this, dialog); 47 | 48 | initView(); 49 | return dialog; 50 | } 51 | 52 | @Override 53 | public void onStart() { 54 | super.onStart(); 55 | Dialog dialog = getDialog(); 56 | if (dialog != null) { 57 | Window window = dialog.getWindow(); 58 | if (window != null) { 59 | window.setBackgroundDrawableResource(android.R.color.transparent); 60 | WindowManager.LayoutParams params = window.getAttributes(); 61 | params.gravity = Gravity.BOTTOM; 62 | params.width = WindowManager.LayoutParams.MATCH_PARENT; 63 | params.height = WindowManager.LayoutParams.WRAP_CONTENT; 64 | window.setAttributes(params); 65 | } 66 | } 67 | } 68 | 69 | @Override 70 | public void onResume() { 71 | super.onResume(); 72 | rg_title.check(R.id.rb_effect); 73 | } 74 | 75 | private void initView() { 76 | rg_title.setOnCheckedChangeListener(this); 77 | rv_changer_grid.setOnItemClickListener(this); 78 | } 79 | 80 | @Override 81 | public void onCheckedChanged(RadioGroup group, int checkedId) { 82 | switch (checkedId) { 83 | case R.id.rb_effect: 84 | rv_changer_grid.showEffect(mEffectSelectedIndex); 85 | break; 86 | case R.id.rb_beautify: 87 | rv_changer_grid.showBeautify(mBeautifySelectedIndex); 88 | break; 89 | } 90 | } 91 | 92 | @Override 93 | public void onItemClick(int position, int value) { 94 | RtcManager manager = ChatRoomManager.instance(mContext).getRtcManager(); 95 | switch (rg_title.getCheckedRadioButtonId()) { 96 | case R.id.rb_effect: 97 | mEffectSelectedIndex = position; 98 | manager.setReverbPreset(value); 99 | break; 100 | case R.id.rb_beautify: 101 | mBeautifySelectedIndex = position; 102 | manager.setVoiceChanger(value); 103 | break; 104 | } 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/activity_chat_room.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 27 | 28 | 36 | 37 | 50 | 51 | 52 | 53 | 62 | 63 | 69 | 70 | 76 | 77 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /iOS/AgoraChatRoom/VoiceChangerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceChangerViewController.swift 3 | // AgoraChatRoom 4 | // 5 | // Created by LXH on 2019/11/27. 6 | // Copyright © 2019 CavanSu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import AgoraRtcKit 12 | 13 | class VoiceChangerViewController: UIViewController { 14 | @IBOutlet weak var gridVoiceEffect: UICollectionView! 15 | @IBOutlet weak var effect: UIButton! 16 | @IBOutlet weak var lineEffect: UIView! 17 | @IBOutlet weak var beautify: UIButton! 18 | @IBOutlet weak var lineBeautify: UIView! 19 | 20 | private var isEffect = true 21 | private var mEffectSelectedIndex = 0 22 | private var mBeautifySelectedIndex = 0 23 | 24 | func showEffect(_ selectedIndex: Int) { 25 | isEffect = true 26 | mEffectSelectedIndex = selectedIndex 27 | gridVoiceEffect.reloadData() 28 | } 29 | 30 | func showBeautify(_ selectedIndex: Int) { 31 | isEffect = false 32 | mBeautifySelectedIndex = selectedIndex 33 | gridVoiceEffect.reloadData() 34 | } 35 | 36 | private func resetViews() { 37 | effect.isSelected = isEffect 38 | lineEffect.isHidden = !isEffect 39 | beautify.isSelected = !isEffect 40 | lineBeautify.isHidden = isEffect 41 | } 42 | 43 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 44 | super.touchesEnded(touches, with: event) 45 | dismiss(animated: true) 46 | } 47 | 48 | @IBAction func onEffectClick(_ sender: UIButton) { 49 | showEffect(mEffectSelectedIndex) 50 | resetViews() 51 | } 52 | 53 | @IBAction func onBeautifyClick(_ sender: UIButton) { 54 | showBeautify(mBeautifySelectedIndex) 55 | resetViews() 56 | } 57 | } 58 | 59 | extension VoiceChangerViewController: UICollectionViewDelegate { 60 | // MARK: - UICollectionViewDelegate 61 | 62 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 63 | let position = indexPath.item 64 | if isEffect { 65 | if position == mEffectSelectedIndex { 66 | return 67 | } 68 | let oldPosition = mEffectSelectedIndex 69 | mEffectSelectedIndex = position 70 | gridVoiceEffect.reloadItems(at: [IndexPath(item: oldPosition, section: 0), indexPath]) 71 | 72 | let type = VoiceEffect.allCases[position].rawValue 73 | ChatRoomManager.shared.getRtcManager().setReverbPreset(type) 74 | } else { 75 | if position == mBeautifySelectedIndex { 76 | return 77 | } 78 | let oldPosition = mBeautifySelectedIndex 79 | mBeautifySelectedIndex = position 80 | gridVoiceEffect.reloadItems(at: [IndexPath(item: oldPosition, section: 0), indexPath]) 81 | 82 | let type = VoiceBeautify.allCases[position].rawValue 83 | ChatRoomManager.shared.getRtcManager().setVoiceChanger(type) 84 | } 85 | } 86 | } 87 | 88 | extension VoiceChangerViewController: UICollectionViewDataSource { 89 | // MARK: - UICollectionViewDataSource 90 | 91 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 92 | isEffect ? VoiceEffect.allCases.count : VoiceBeautify.allCases.count 93 | } 94 | 95 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 96 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "VoiceChangerCell", for: indexPath) as! VoiceChangerCell 97 | 98 | cell.update(isEffect, mEffectSelectedIndex, mBeautifySelectedIndex, indexPath.item) 99 | 100 | return cell 101 | } 102 | } 103 | 104 | extension VoiceChangerViewController: UICollectionViewDelegateFlowLayout { 105 | // MARK: - UICollectionViewDelegateFlowLayout 106 | 107 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 108 | let width = UIApplication.shared.windows[0].bounds.size.width 109 | return CGSize.init(width: Int((width - 19 * 4) / 3), height: 44) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Android/app/src/main/res/layout/layout_chat_room_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 25 | 26 | 42 | 43 | 57 | 58 | 67 | 68 | 77 | 78 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Android/app/src/main/java/io/agora/chatroom/adapter/SeatGridAdapter.java: -------------------------------------------------------------------------------- 1 | package io.agora.chatroom.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.recyclerview.widget.RecyclerView; 11 | 12 | import java.util.List; 13 | 14 | import butterknife.BindView; 15 | import butterknife.ButterKnife; 16 | import de.hdodenhof.circleimageview.CircleImageView; 17 | import io.agora.chatroom.R; 18 | import io.agora.chatroom.model.ChannelData; 19 | import io.agora.chatroom.model.Seat; 20 | import io.agora.chatroom.manager.ChatRoomManager; 21 | import io.agora.chatroom.widget.SpreadView; 22 | 23 | public class SeatGridAdapter extends RecyclerView.Adapter { 24 | 25 | private LayoutInflater mInflater; 26 | private OnItemClickListener mListener; 27 | 28 | private ChannelData mChannelData; 29 | 30 | public SeatGridAdapter(Context context) { 31 | mInflater = LayoutInflater.from(context); 32 | mChannelData = ChatRoomManager.instance(context).getChannelData(); 33 | } 34 | 35 | @NonNull 36 | @Override 37 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 38 | View view = mInflater.inflate(R.layout.layout_item_seat, parent, false); 39 | return new ViewHolder(view); 40 | } 41 | 42 | @Override 43 | public int getItemCount() { 44 | if (mChannelData == null) return 0; 45 | return mChannelData.getSeatArray().length; 46 | } 47 | 48 | @Override 49 | public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List payloads) { 50 | super.onBindViewHolder(holder, position, payloads); 51 | if (payloads.size() > 0) 52 | holder.view_anim.startAnimation(); 53 | } 54 | 55 | @Override 56 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 57 | Seat seat = mChannelData.getSeatArray()[position]; 58 | 59 | if (seat != null) { 60 | if (seat.isClosed()) { 61 | holder.iv_seat.setImageResource(R.mipmap.ic_ban); 62 | holder.iv_mute.setVisibility(View.GONE); 63 | } else { 64 | String userId = seat.getUserId(); 65 | if (mChannelData.isUserOnline(userId)) { 66 | holder.iv_seat.setImageResource(mChannelData.getMemberAvatar(userId)); 67 | holder.iv_mute.setVisibility(mChannelData.isUserMuted(userId) ? View.VISIBLE : View.GONE); 68 | } else { 69 | holder.iv_seat.setImageResource(R.mipmap.ic_join); 70 | holder.iv_mute.setVisibility(View.GONE); 71 | } 72 | } 73 | } else { 74 | holder.iv_seat.setImageResource(R.mipmap.ic_join); 75 | holder.iv_mute.setVisibility(View.GONE); 76 | } 77 | 78 | holder.iv_seat.setOnClickListener(view -> { 79 | if (mListener != null) 80 | mListener.onItemClick(holder.view, position, seat); 81 | }); 82 | } 83 | 84 | public void setOnItemClickListener(OnItemClickListener listener) { 85 | mListener = listener; 86 | } 87 | 88 | public void notifyItemChanged(String userId, boolean animated) { 89 | int index = mChannelData.indexOfSeatArray(userId); 90 | if (index >= 0) { 91 | if (animated) 92 | notifyItemChanged(index, true); 93 | else 94 | notifyItemChanged(index); 95 | } 96 | } 97 | 98 | class ViewHolder extends RecyclerView.ViewHolder { 99 | View view; 100 | @BindView(R.id.view_anim) 101 | SpreadView view_anim; 102 | @BindView(R.id.iv_seat) 103 | CircleImageView iv_seat; 104 | @BindView(R.id.iv_mute) 105 | ImageView iv_mute; 106 | 107 | ViewHolder(View view) { 108 | super(view); 109 | this.view = view; 110 | ButterKnife.bind(this, view); 111 | } 112 | } 113 | 114 | public interface OnItemClickListener { 115 | void onItemClick(View view, int position, Seat seat); 116 | } 117 | 118 | } 119 | --------------------------------------------------------------------------------