├── .github ├── ci-gradle.properties.gpg ├── flat.keystore.gpg ├── google-services.json.gpg ├── scripts │ ├── decrypt_secret.sh │ └── project_config.sh └── workflows │ └── flat.yaml ├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml └── inspectionProfiles │ └── Project_Default.xml ├── CHANGELOG.md ├── LICENSE ├── README-zh.md ├── README.md ├── RELEASING.md ├── app ├── build.gradle ├── proguard-rules.pro ├── schemas │ └── io.agora.flat.data.AppDatabase │ │ ├── 1.json │ │ └── 2.json └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── agora │ │ └── flat │ │ └── ExampleInstrumentedTest.kt │ ├── flat │ └── AndroidManifest.xml │ ├── flint │ ├── ic_launcher-playstore.png │ └── res │ │ ├── drawable-xxhdpi │ │ └── ic_flat_logo.png │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ └── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── countries.json │ │ ├── flatboard │ │ │ ├── b280a53 │ │ │ ├── e33d6c2 │ │ │ ├── e7b0b24 │ │ │ ├── index.html │ │ │ ├── whiteboard-bridge.css │ │ │ └── whiteboard-bridge.js │ │ ├── lottie │ │ │ ├── images │ │ │ │ ├── img_0.png │ │ │ │ └── img_1.png │ │ │ └── reward.json │ │ └── reward.mp3 │ ├── ic_launcher-playstore.png │ ├── java │ │ └── io │ │ │ └── agora │ │ │ └── flat │ │ │ ├── AppInitializers.kt │ │ │ ├── Config.kt │ │ │ ├── Constants.kt │ │ │ ├── MainApplication.kt │ │ │ ├── PostLoginInitializers.kt │ │ │ ├── common │ │ │ ├── AppNavigation.kt │ │ │ ├── FlatErrorCode.kt │ │ │ ├── FlatException.kt │ │ │ ├── Navigator.kt │ │ │ ├── android │ │ │ │ ├── AndroidClipboardController.kt │ │ │ │ ├── AndroidDownloader.kt │ │ │ │ ├── AppCoroutineDispatchers.kt │ │ │ │ ├── CallingCodeManager.kt │ │ │ │ ├── DarkModeManager.kt │ │ │ │ ├── I18NFetcher.kt │ │ │ │ ├── IntentHandler.kt │ │ │ │ ├── LanguageManager.kt │ │ │ │ ├── ProtocolUrlManager.kt │ │ │ │ └── WindowFocusObserver.kt │ │ │ ├── board │ │ │ │ ├── AgoraBoardRoom.kt │ │ │ │ ├── BoardEntity.kt │ │ │ │ ├── StorageState.kt │ │ │ │ └── WhiteSyncedState.kt │ │ │ ├── error │ │ │ │ └── FlatErrorHandler.kt │ │ │ ├── login │ │ │ │ ├── GithubEntryActivity.kt │ │ │ │ ├── LoginActivityHandler.kt │ │ │ │ ├── LoginManager.kt │ │ │ │ ├── LoginModels.kt │ │ │ │ └── UserBindingHandler.kt │ │ │ ├── rtc │ │ │ │ ├── AgoraRtc.kt │ │ │ │ ├── Misc.kt │ │ │ │ ├── RtcEvent.kt │ │ │ │ ├── RtcJoinOptions.kt │ │ │ │ └── RtcVideoController.kt │ │ │ ├── rtm │ │ │ │ ├── AgoraRtm.kt │ │ │ │ ├── ClassRtmEvent.kt │ │ │ │ ├── Message.kt │ │ │ │ ├── Misc.kt │ │ │ │ └── RtmMember.kt │ │ │ ├── upload │ │ │ │ └── UploadManager.kt │ │ │ └── version │ │ │ │ ├── AgreementFetcher.kt │ │ │ │ ├── VersionCheckResult.kt │ │ │ │ └── VersionChecker.kt │ │ │ ├── data │ │ │ ├── AppDatabase.kt │ │ │ ├── AppEnv.kt │ │ │ ├── AppKVCenter.kt │ │ │ ├── Result.kt │ │ │ ├── RetrofitExtensions.kt │ │ │ ├── ServiceFetcher.kt │ │ │ ├── dao │ │ │ │ ├── RecordHistoryDao.kt │ │ │ │ └── RoomConfigDao.kt │ │ │ ├── manager │ │ │ │ └── JoinRoomRecordManager.kt │ │ │ ├── model │ │ │ │ ├── AgreementsResp.kt │ │ │ │ ├── AuthUUIDReq.kt │ │ │ │ ├── AvatarData.kt │ │ │ │ ├── BaseReq.kt │ │ │ │ ├── BaseResp.kt │ │ │ │ ├── CallingCode.kt │ │ │ │ ├── CancelRoomReq.kt │ │ │ │ ├── ClassModeType.kt │ │ │ │ ├── CloudFile.kt │ │ │ │ ├── CloudTypes.kt │ │ │ │ ├── CoursewareType.kt │ │ │ │ ├── DocsType.kt │ │ │ │ ├── EmailBindReq.kt │ │ │ │ ├── EmailCodeReq.kt │ │ │ │ ├── EmailPasswordReq.kt │ │ │ │ ├── EmailRegisterReq.kt │ │ │ │ ├── EmailReq.kt │ │ │ │ ├── InviteInfo.kt │ │ │ │ ├── JoinRoomRecord.kt │ │ │ │ ├── JoinRoomReq.kt │ │ │ │ ├── LoadState.kt │ │ │ │ ├── LoginCheckReq.kt │ │ │ │ ├── LoginHistory.kt │ │ │ │ ├── LoginPlatform.kt │ │ │ │ ├── MessageCountResp.kt │ │ │ │ ├── MessageListResp.kt │ │ │ │ ├── MessageQueryHistoryReq.kt │ │ │ │ ├── MessageQueryHistoryResp.kt │ │ │ │ ├── NetworkRoomUser.kt │ │ │ │ ├── Order.kt │ │ │ │ ├── PeriodicSubRoom.kt │ │ │ │ ├── PeriodicSubRoomReq.kt │ │ │ │ ├── PhoneOrEmailInfo.kt │ │ │ │ ├── PhonePasswordReq.kt │ │ │ │ ├── PhoneRegisterReq.kt │ │ │ │ ├── PhoneReq.kt │ │ │ │ ├── PhoneSmsCodeReq.kt │ │ │ │ ├── PureRoomReq.kt │ │ │ │ ├── PureToken.kt │ │ │ │ ├── RecordAcquireReq.kt │ │ │ │ ├── RecordAcquireRespData.kt │ │ │ │ ├── RecordAgoraParams.kt │ │ │ │ ├── RecordHistory.kt │ │ │ │ ├── RecordInfo.kt │ │ │ │ ├── RecordQueryRespData.kt │ │ │ │ ├── RecordReq.kt │ │ │ │ ├── RecordStartReq.kt │ │ │ │ ├── RecordStartRespData.kt │ │ │ │ ├── RecordStopRespData.kt │ │ │ │ ├── RecordUpdateLayoutReq.kt │ │ │ │ ├── RegionConfigs.kt │ │ │ │ ├── RemoveBindingReq.kt │ │ │ │ ├── RespNoData.kt │ │ │ │ ├── RoomConfig.kt │ │ │ │ ├── RoomCountResp.kt │ │ │ │ ├── RoomCreateReq.kt │ │ │ │ ├── RoomCreateRespData.kt │ │ │ │ ├── RoomDetailOrdinary.kt │ │ │ │ ├── RoomDetailOrdinaryReq.kt │ │ │ │ ├── RoomDetailPeriodic.kt │ │ │ │ ├── RoomDetailPeriodicReq.kt │ │ │ │ ├── RoomDocs.kt │ │ │ │ ├── RoomInfo.kt │ │ │ │ ├── RoomPeriodic.kt │ │ │ │ ├── RoomPlayInfo.kt │ │ │ │ ├── RoomStatus.kt │ │ │ │ ├── RoomType.kt │ │ │ │ ├── RoomUser.kt │ │ │ │ ├── RoomUsersReq.kt │ │ │ │ ├── RtmCensorReq.kt │ │ │ │ ├── RtmCensorRespData.kt │ │ │ │ ├── RtmQueryMessage.kt │ │ │ │ ├── SetPasswordReq.kt │ │ │ │ ├── StreamAgreement.kt │ │ │ │ ├── StreamAgreementReq.kt │ │ │ │ ├── UserBindings.kt │ │ │ │ ├── UserInfo.kt │ │ │ │ ├── UserInfoRebind.kt │ │ │ │ ├── UserInfoWithToken.kt │ │ │ │ ├── UserRenameReq.kt │ │ │ │ ├── UserTokenData.kt │ │ │ │ ├── WeChatCallbackReq.kt │ │ │ │ ├── Week.kt │ │ │ │ └── WindowAppItem.kt │ │ │ └── repository │ │ │ │ ├── CloudRecordRepository.kt │ │ │ │ ├── CloudStorageRepository.kt │ │ │ │ ├── MessageRepository.kt │ │ │ │ ├── MiscRepository.kt │ │ │ │ ├── RoomConfigRepository.kt │ │ │ │ ├── RoomRepository.kt │ │ │ │ └── UserRepository.kt │ │ │ ├── di │ │ │ ├── ActivityModule.kt │ │ │ ├── ActivityRetainedModuleBinds.kt │ │ │ ├── AppModule.kt │ │ │ ├── AppModuleBinds.kt │ │ │ ├── GlobalInstanceProvider.kt │ │ │ ├── NetworkModule.kt │ │ │ ├── UserModule.kt │ │ │ └── interfaces │ │ │ │ ├── BoardRoom.kt │ │ │ │ ├── NetworkObserver.kt │ │ │ │ ├── RtcApi.kt │ │ │ │ ├── RtmApi.kt │ │ │ │ └── SyncedClassState.kt │ │ │ ├── event │ │ │ ├── ClassroomEvent.kt │ │ │ ├── EventBus.kt │ │ │ ├── MessagesAppended.kt │ │ │ ├── NoOptPermission.kt │ │ │ ├── RoomsUpdated.kt │ │ │ ├── UserBindingsUpdated.kt │ │ │ └── UserUpdated.kt │ │ │ ├── http │ │ │ ├── HeaderProvider.kt │ │ │ ├── api │ │ │ │ ├── CloudRecordService.kt │ │ │ │ ├── CloudStorageServiceV2.kt │ │ │ │ ├── MessageService.kt │ │ │ │ ├── MiscService.kt │ │ │ │ ├── RoomService.kt │ │ │ │ └── UserService.kt │ │ │ ├── interceptor │ │ │ │ ├── AgoraMessageInterceptor.kt │ │ │ │ ├── HeaderInterceptor.kt │ │ │ │ └── OtherInterceptor.kt │ │ │ └── model │ │ │ │ ├── CloudConvertFinishReq.kt │ │ │ │ ├── CloudConvertStartReq.kt │ │ │ │ ├── CloudConvertStartResp.kt │ │ │ │ ├── CloudFileDeleteReq.kt │ │ │ │ ├── CloudFileMoveReq.kt │ │ │ │ ├── CloudFileRenameReq.kt │ │ │ │ ├── CloudListFilesReq.kt │ │ │ │ ├── CloudListFilesResp.kt │ │ │ │ ├── CloudUploadFinishReq.kt │ │ │ │ ├── CloudUploadStartReq.kt │ │ │ │ ├── CloudUploadStartResp.kt │ │ │ │ ├── CloudUploadTempFileStartReq.kt │ │ │ │ └── CreateDirectoryReq.kt │ │ │ ├── ui │ │ │ ├── activity │ │ │ │ ├── CallingCodeActivity.kt │ │ │ │ ├── LoginActivity.kt │ │ │ │ ├── base │ │ │ │ │ ├── BaseAccountViewModel.kt │ │ │ │ │ ├── BaseActivity.kt │ │ │ │ │ ├── BaseComposeActivity.kt │ │ │ │ │ └── BaseViewModel.kt │ │ │ │ ├── bind │ │ │ │ │ ├── EmailBindActivity.kt │ │ │ │ │ └── EmailBindViewModel.kt │ │ │ │ ├── camera │ │ │ │ │ ├── CameraActivity.kt │ │ │ │ │ └── fragments │ │ │ │ │ │ ├── CameraFragment.kt │ │ │ │ │ │ ├── GalleryFragment.kt │ │ │ │ │ │ └── PermissionsFragment.kt │ │ │ │ ├── cloud │ │ │ │ │ ├── list │ │ │ │ │ │ ├── CloudPickFileGrid.kt │ │ │ │ │ │ ├── CloudStorage.kt │ │ │ │ │ │ ├── CloudStorageUiState.kt │ │ │ │ │ │ └── CloudStorageViewModel.kt │ │ │ │ │ ├── preview │ │ │ │ │ │ ├── PreviewAction.kt │ │ │ │ │ │ ├── PreviewActivity.kt │ │ │ │ │ │ ├── PreviewState.kt │ │ │ │ │ │ └── PreviewViewModel.kt │ │ │ │ │ └── uploading │ │ │ │ │ │ ├── Uploading.kt │ │ │ │ │ │ ├── UploadingUIState.kt │ │ │ │ │ │ └── UploadingViewModel.kt │ │ │ │ ├── dev │ │ │ │ │ ├── ComposeTestActivity.kt │ │ │ │ │ ├── DevSettingsActivity.kt │ │ │ │ │ ├── DevToolViewModel.kt │ │ │ │ │ └── DevToolsActivity.kt │ │ │ │ ├── history │ │ │ │ │ ├── History.kt │ │ │ │ │ ├── HistoryUiState.kt │ │ │ │ │ └── HistoryViewModel.kt │ │ │ │ ├── home │ │ │ │ │ ├── ExtInitPage.kt │ │ │ │ │ ├── Home.kt │ │ │ │ │ ├── HomeUiState.kt │ │ │ │ │ ├── HomeViewModel.kt │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ └── MainViewModel.kt │ │ │ │ ├── login │ │ │ │ │ ├── LoginUiAction.kt │ │ │ │ │ └── LoginViewModel.kt │ │ │ │ ├── password │ │ │ │ │ ├── PasswordChangeActivity.kt │ │ │ │ │ ├── PasswordChangeViewModel.kt │ │ │ │ │ ├── PasswordResetActivity.kt │ │ │ │ │ ├── PasswordResetViewModel.kt │ │ │ │ │ └── PasswordSetActivity.kt │ │ │ │ ├── phone │ │ │ │ │ ├── PhoneBindActivity.kt │ │ │ │ │ └── PhoneBindViewModel.kt │ │ │ │ ├── play │ │ │ │ │ ├── AcceptHandupAdapter.kt │ │ │ │ │ ├── BaseComponent.kt │ │ │ │ │ ├── ClassCloudComponent.kt │ │ │ │ │ ├── ClassCloudViewModel.kt │ │ │ │ │ ├── ClassRoomActivity.kt │ │ │ │ │ ├── ClassRoomViewModel.kt │ │ │ │ │ ├── CloudStorageAdapter.kt │ │ │ │ │ ├── ExtComponent.kt │ │ │ │ │ ├── ExtensionViewModel.kt │ │ │ │ │ ├── FlatControllerGroup.kt │ │ │ │ │ ├── MessageAdapter.kt │ │ │ │ │ ├── RtcComponent.kt │ │ │ │ │ ├── RtmComponent.kt │ │ │ │ │ ├── ToolComponent.kt │ │ │ │ │ ├── UserListAdapter.kt │ │ │ │ │ ├── UserVideoAdapter.kt │ │ │ │ │ ├── WhiteboardComponent.kt │ │ │ │ │ ├── WindowAppAdapter.kt │ │ │ │ │ └── WindowLocalState.kt │ │ │ │ ├── playback │ │ │ │ │ ├── ReplayActivity.kt │ │ │ │ │ ├── ReplayPlayerComponent.kt │ │ │ │ │ ├── ReplayToolComponent.kt │ │ │ │ │ └── ReplayViewModel.kt │ │ │ │ ├── register │ │ │ │ │ ├── RegisterActivity.kt │ │ │ │ │ ├── RegisterProfileActivity.kt │ │ │ │ │ ├── RegisterProfileViewModel.kt │ │ │ │ │ └── RegisterViewModel.kt │ │ │ │ ├── room │ │ │ │ │ ├── CreateRoomActivity.kt │ │ │ │ │ ├── JoinRoomActivity.kt │ │ │ │ │ ├── RoomDetailActivity.kt │ │ │ │ │ └── SubscribeRoomActivity.kt │ │ │ │ └── setting │ │ │ │ │ ├── AboutUsActivity.kt │ │ │ │ │ ├── AccountSecurityActivity.kt │ │ │ │ │ ├── CallTestActivity.kt │ │ │ │ │ ├── DarkModeActivity.kt │ │ │ │ │ ├── EditNameActivity.kt │ │ │ │ │ ├── FeedbackActivity.kt │ │ │ │ │ ├── LanguageActivity.kt │ │ │ │ │ ├── Settings.kt │ │ │ │ │ ├── SettingsUiAction.kt │ │ │ │ │ ├── UserInfoActivity.kt │ │ │ │ │ ├── UserProfile.kt │ │ │ │ │ └── WebViewActivity.kt │ │ │ ├── animator │ │ │ │ └── SimpleAnimator.kt │ │ │ ├── compose │ │ │ │ ├── ComposeExtension.kt │ │ │ │ ├── ComposePreviewData.kt │ │ │ │ ├── ComposeVideoPlayer.kt │ │ │ │ ├── CompositionLocal.kt │ │ │ │ ├── CustomInteractionSource.kt │ │ │ │ ├── DevicePreview.kt │ │ │ │ ├── EmptyView.kt │ │ │ │ ├── FlatAgreement.kt │ │ │ │ ├── FlatAvatar.kt │ │ │ │ ├── FlatButton.kt │ │ │ │ ├── FlatCircularProgressIndicator.kt │ │ │ │ ├── FlatClickableText.kt │ │ │ │ ├── FlatDialog.kt │ │ │ │ ├── FlatDivider.kt │ │ │ │ ├── FlatIcon.kt │ │ │ │ ├── FlatLoading.kt │ │ │ │ ├── FlatPage.kt │ │ │ │ ├── FlatRoomItem.kt │ │ │ │ ├── FlatSpacer.kt │ │ │ │ ├── FlatSwipeRefresh.kt │ │ │ │ ├── FlatText.kt │ │ │ │ ├── FlatTextFiled.kt │ │ │ │ ├── FlatTopAppBar.kt │ │ │ │ ├── PhoneOrEmailPassword.kt │ │ │ │ ├── StreamCollectDialog.kt │ │ │ │ ├── UpdateDialog.kt │ │ │ │ └── WheelPicker.kt │ │ │ ├── manager │ │ │ │ ├── RecordManager.kt │ │ │ │ ├── RoomErrorManager.kt │ │ │ │ ├── RoomLayoutStateManager.kt │ │ │ │ ├── RoomOverlayManager.kt │ │ │ │ ├── RoomStateManager.kt │ │ │ │ ├── UserManager.kt │ │ │ │ ├── UserQuery.kt │ │ │ │ └── WindowsDragManager.kt │ │ │ ├── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Grid.kt │ │ │ │ ├── Shape.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ ├── util │ │ │ │ ├── ObservableLoadingCounter.kt │ │ │ │ └── UiMessage.kt │ │ │ ├── view │ │ │ │ ├── AudioVolumeView.kt │ │ │ │ ├── ClassDialogFragment.kt │ │ │ │ ├── FlatDrawables.kt │ │ │ │ ├── FooterAdapter.kt │ │ │ │ ├── InviteDialog.kt │ │ │ │ ├── LoadingDialog.kt │ │ │ │ ├── MessageListView.kt │ │ │ │ ├── OwnerExitDialog.kt │ │ │ │ ├── PaddingItemDecoration.kt │ │ │ │ ├── ReplayExitDialog.kt │ │ │ │ ├── RequestDeviceDialog.kt │ │ │ │ ├── RoomExitDialog.kt │ │ │ │ ├── StrokeSeeker.kt │ │ │ │ ├── TimeStateLayout.kt │ │ │ │ ├── UserWindowLayout.kt │ │ │ │ └── room │ │ │ │ │ └── ClickHandleView.kt │ │ │ └── viewmodel │ │ │ │ ├── AccountSecurityViewModel.kt │ │ │ │ ├── CallTestViewModel.kt │ │ │ │ ├── ChatMessageManager.kt │ │ │ │ ├── CreateRoomViewModel.kt │ │ │ │ ├── DarkModeViewModel.kt │ │ │ │ ├── EditNameViewModel.kt │ │ │ │ ├── FeedbackViewModel.kt │ │ │ │ ├── JoinRoomViewModel.kt │ │ │ │ ├── LanguageViewModel.kt │ │ │ │ ├── MessageQuery.kt │ │ │ │ ├── MessageViewModel.kt │ │ │ │ ├── RoomDetailViewModel.kt │ │ │ │ ├── SettingsViewModel.kt │ │ │ │ ├── UserInfoViewModel.kt │ │ │ │ └── UserViewModel.kt │ │ │ ├── util │ │ │ ├── Combine.kt │ │ │ ├── ContextExtensions.kt │ │ │ ├── CoroutinesExtensions.kt │ │ │ ├── DateUtils.kt │ │ │ ├── FlatExtensions.kt │ │ │ ├── FlatFormatter.kt │ │ │ ├── JsonUtils.kt │ │ │ ├── KeyboardHeightProvider.kt │ │ │ ├── Ticker.kt │ │ │ ├── UrlUtils.kt │ │ │ └── ViewExtensions.kt │ │ │ └── wxapi │ │ │ └── WXEntryActivity.kt │ └── res │ │ ├── color │ │ ├── color_camera_button.xml │ │ └── color_class_room_icon.xml │ │ ├── drawable-xxhdpi │ │ ├── ic_arrow_right_red.png │ │ ├── ic_class_room_message.png │ │ ├── ic_class_room_video.png │ │ ├── ic_class_room_voice.png │ │ ├── ic_cloudstorage.png │ │ ├── ic_flat_logo.png │ │ ├── ic_github_login.png │ │ ├── ic_google_login.png │ │ ├── ic_home.png │ │ ├── ic_item_checked.png │ │ ├── ic_item_unchecked.png │ │ ├── ic_loading.png │ │ ├── ic_message_send.png │ │ ├── ic_room.png │ │ ├── ic_room_teacher_leave.png │ │ ├── ic_room_type.png │ │ ├── ic_upload_cancel.png │ │ ├── ic_upload_retry.png │ │ ├── ic_upload_success.png │ │ ├── ic_user_profile_feedback.png │ │ ├── ic_wechat_login.png │ │ ├── img_big_class.png │ │ ├── img_cloud_storage_no_file.png │ │ ├── img_home_no_history.png │ │ ├── img_home_no_room.png │ │ ├── img_one_to_one.png │ │ ├── img_pad_login_dark.png │ │ ├── img_pad_login_light.png │ │ ├── img_room_video_closed_head.png │ │ ├── img_room_video_closed_head_big.png │ │ └── img_small_class.png │ │ ├── drawable-xxxhdpi │ │ ├── ic_cloud_file_audio.png │ │ ├── ic_cloud_file_folder.png │ │ ├── ic_cloud_file_image.png │ │ ├── ic_cloud_file_others.png │ │ ├── ic_cloud_file_pdf.png │ │ ├── ic_cloud_file_ppt.png │ │ ├── ic_cloud_file_video.png │ │ ├── ic_cloud_file_word.png │ │ ├── ic_cloud_upload_failure.png │ │ ├── ic_cloud_upload_success.png │ │ ├── ic_register_avatar.png │ │ ├── ic_upload_file_audio.png │ │ ├── ic_upload_file_doc.png │ │ ├── ic_upload_file_image.png │ │ ├── ic_upload_file_video.png │ │ ├── img_login_slogan_dark_en.png │ │ ├── img_login_slogan_dark_zh.png │ │ ├── img_login_slogan_light_en.png │ │ ├── img_login_slogan_light_zh.png │ │ ├── img_login_slogan_pad_dark_en.png │ │ ├── img_login_slogan_pad_dark_zh.png │ │ ├── img_login_slogan_pad_light_en.png │ │ └── img_login_slogan_pad_light_zh.png │ │ ├── drawable │ │ ├── flat_switch_thumb.xml │ │ ├── flat_switch_track.xml │ │ ├── ic_apps_clock.xml │ │ ├── ic_apps_code.xml │ │ ├── ic_apps_dice.xml │ │ ├── ic_apps_geometry.xml │ │ ├── ic_apps_mindmap.xml │ │ ├── ic_apps_question.xml │ │ ├── ic_apps_rich_text.xml │ │ ├── ic_arrow_left.xml │ │ ├── ic_arrow_right.xml │ │ ├── ic_camera_back.xml │ │ ├── ic_camera_shutter_selector.xml │ │ ├── ic_camera_switch.xml │ │ ├── ic_class_audio_volume.xml │ │ ├── ic_class_cloud_arrow_right.xml │ │ ├── ic_class_room_camera_off.xml │ │ ├── ic_class_room_camera_on.xml │ │ ├── ic_class_room_chat.xml │ │ ├── ic_class_room_close.xml │ │ ├── ic_class_room_cloud.xml │ │ ├── ic_class_room_exit.xml │ │ ├── ic_class_room_eye_off.xml │ │ ├── ic_class_room_eye_on.xml │ │ ├── ic_class_room_icon_bg.xml │ │ ├── ic_class_room_icon_bg_selected.xml │ │ ├── ic_class_room_invite.xml │ │ ├── ic_class_room_lecture.xml │ │ ├── ic_class_room_mic_off.xml │ │ ├── ic_class_room_mic_on.xml │ │ ├── ic_class_room_record.xml │ │ ├── ic_class_room_record_stop.xml │ │ ├── ic_class_room_setting.xml │ │ ├── ic_class_room_take_photo.xml │ │ ├── ic_class_room_user_avatar.xml │ │ ├── ic_class_room_user_invite.xml │ │ ├── ic_class_room_user_list.xml │ │ ├── ic_cloud_list_add.xml │ │ ├── ic_cloud_list_edit.xml │ │ ├── ic_cloud_list_new_folder.xml │ │ ├── ic_cloud_list_option.xml │ │ ├── ic_cloud_list_unloading.xml │ │ ├── ic_cloud_storage_convert_failure.xml │ │ ├── ic_cloud_storage_converting.xml │ │ ├── ic_dialog_close.xml │ │ ├── ic_float_red_dot.xml │ │ ├── ic_handup_ing_disable.xml │ │ ├── ic_has_record.xml │ │ ├── ic_history.xml │ │ ├── ic_home_calendar.xml │ │ ├── ic_home_join_room.xml │ │ ├── ic_home_quick_start.xml │ │ ├── ic_home_subscribe_room.xml │ │ ├── ic_item_check_selector.xml │ │ ├── ic_loading_rotate.xml │ │ ├── ic_login_arrow_down.xml │ │ ├── ic_login_email.xml │ │ ├── ic_login_out.xml │ │ ├── ic_login_password.xml │ │ ├── ic_login_password_hide.xml │ │ ├── ic_login_password_show.xml │ │ ├── ic_login_sms_code.xml │ │ ├── ic_main_cloud_normal.xml │ │ ├── ic_main_cloud_selected.xml │ │ ├── ic_main_home_normal.xml │ │ ├── ic_main_home_selected.xml │ │ ├── ic_message_muted.xml │ │ ├── ic_message_muted_selector.xml │ │ ├── ic_message_unmuted.xml │ │ ├── ic_mute_all.xml │ │ ├── ic_network_status.xml │ │ ├── ic_playback_exit.xml │ │ ├── ic_playback_pause.xml │ │ ├── ic_playback_start.xml │ │ ├── ic_record_arrow_down.xml │ │ ├── ic_record_stop.xml │ │ ├── ic_red_dot.xml │ │ ├── ic_restore_userwindow.xml │ │ ├── ic_room_audio_closed.xml │ │ ├── ic_room_audio_closed_gray.xml │ │ ├── ic_room_audio_opened.xml │ │ ├── ic_room_audio_opened_gray.xml │ │ ├── ic_room_audio_state_gray_selector.xml │ │ ├── ic_room_audio_state_selector.xml │ │ ├── ic_room_cloud_storage_add.xml │ │ ├── ic_room_detail_copy.xml │ │ ├── ic_room_detail_id.xml │ │ ├── ic_room_detail_state.xml │ │ ├── ic_room_detail_time.xml │ │ ├── ic_room_detail_type.xml │ │ ├── ic_room_hand_up_normal.xml │ │ ├── ic_room_hand_up_selected.xml │ │ ├── ic_room_hand_up_selector.xml │ │ ├── ic_room_mic_closed.xml │ │ ├── ic_room_play.xml │ │ ├── ic_room_start_class.xml │ │ ├── ic_room_type_big_class.xml │ │ ├── ic_room_type_ono_to_one.xml │ │ ├── ic_room_type_small_class.xml │ │ ├── ic_room_user_list_handup_close.xml │ │ ├── ic_room_userlist_handup_agree.xml │ │ ├── ic_room_video_closed.xml │ │ ├── ic_room_video_closed_big.xml │ │ ├── ic_room_video_closed_gray.xml │ │ ├── ic_room_video_opened.xml │ │ ├── ic_room_video_opened_gray.xml │ │ ├── ic_room_video_state_gray_selector.xml │ │ ├── ic_room_video_state_selector.xml │ │ ├── ic_room_video_to_collapse.xml │ │ ├── ic_room_video_to_expand.xml │ │ ├── ic_s_draw_allowed.xml │ │ ├── ic_s_draw_forbidden.xml │ │ ├── ic_send_reward.xml │ │ ├── ic_settings_about_us.xml │ │ ├── ic_settings_app_version.xml │ │ ├── ic_settings_close_account.xml │ │ ├── ic_settings_dark_light.xml │ │ ├── ic_settings_debug.xml │ │ ├── ic_settings_info_gathering.xml │ │ ├── ic_settings_info_third_party.xml │ │ ├── ic_settings_language.xml │ │ ├── ic_settings_network_acceleration.xml │ │ ├── ic_settings_privacy_policy.xml │ │ ├── ic_settings_security.xml │ │ ├── ic_settings_stream_analysis.xml │ │ ├── ic_settings_term_of_service.xml │ │ ├── ic_settings_user_exit.xml │ │ ├── ic_shutter_focused.xml │ │ ├── ic_shutter_normal.xml │ │ ├── ic_shutter_pressed.xml │ │ ├── ic_text_filed_clear.xml │ │ ├── ic_title_close.xml │ │ ├── ic_toolbox_apps.xml │ │ ├── ic_toolbox_arrow_normal.xml │ │ ├── ic_toolbox_arrow_selected.xml │ │ ├── ic_toolbox_arrow_selector.xml │ │ ├── ic_toolbox_circle_normal.xml │ │ ├── ic_toolbox_circle_selected.xml │ │ ├── ic_toolbox_circle_selector.xml │ │ ├── ic_toolbox_clear_normal.xml │ │ ├── ic_toolbox_clear_selected.xml │ │ ├── ic_toolbox_clear_selector.xml │ │ ├── ic_toolbox_clicker_normal.xml │ │ ├── ic_toolbox_clicker_selected.xml │ │ ├── ic_toolbox_clicker_selector.xml │ │ ├── ic_toolbox_eraser_normal.xml │ │ ├── ic_toolbox_eraser_selected.xml │ │ ├── ic_toolbox_eraser_selector.xml │ │ ├── ic_toolbox_exit.xml │ │ ├── ic_toolbox_ext_cloudservice_normal.xml │ │ ├── ic_toolbox_ext_cloudservice_selected.xml │ │ ├── ic_toolbox_ext_cloudservice_selector.xml │ │ ├── ic_toolbox_ext_collapsed.xml │ │ ├── ic_toolbox_ext_expanded.xml │ │ ├── ic_toolbox_ext_hide_video.xml │ │ ├── ic_toolbox_ext_invite_normal.xml │ │ ├── ic_toolbox_ext_invite_selected.xml │ │ ├── ic_toolbox_ext_invite_selector.xml │ │ ├── ic_toolbox_ext_message_normal.xml │ │ ├── ic_toolbox_ext_message_selected.xml │ │ ├── ic_toolbox_ext_message_selector.xml │ │ ├── ic_toolbox_ext_setting_normal.xml │ │ ├── ic_toolbox_ext_setting_selected.xml │ │ ├── ic_toolbox_ext_setting_selector.xml │ │ ├── ic_toolbox_ext_state_idle_normal.xml │ │ ├── ic_toolbox_ext_state_selector.xml │ │ ├── ic_toolbox_ext_state_state_started_normal.xml │ │ ├── ic_toolbox_ext_state_state_started_selected.xml │ │ ├── ic_toolbox_ext_userlist_normal.xml │ │ ├── ic_toolbox_ext_userlist_selected.xml │ │ ├── ic_toolbox_ext_userlist_selector.xml │ │ ├── ic_toolbox_ext_viewfollow_normal.xml │ │ ├── ic_toolbox_ext_viewfollow_selected.xml │ │ ├── ic_toolbox_ext_viewfollow_selector.xml │ │ ├── ic_toolbox_file_upload_normal.xml │ │ ├── ic_toolbox_file_upload_selected.xml │ │ ├── ic_toolbox_file_upload_selector.xml │ │ ├── ic_toolbox_hand_normal.xml │ │ ├── ic_toolbox_hand_selected.xml │ │ ├── ic_toolbox_hand_selector.xml │ │ ├── ic_toolbox_laser_normal.xml │ │ ├── ic_toolbox_laser_selected.xml │ │ ├── ic_toolbox_laser_selector.xml │ │ ├── ic_toolbox_line_normal.xml │ │ ├── ic_toolbox_line_selected.xml │ │ ├── ic_toolbox_line_selector.xml │ │ ├── ic_toolbox_nextpage_disabled.xml │ │ ├── ic_toolbox_nextpage_enabled.xml │ │ ├── ic_toolbox_nextpage_selector.xml │ │ ├── ic_toolbox_page_end_disabled.xml │ │ ├── ic_toolbox_page_end_enabled.xml │ │ ├── ic_toolbox_page_end_selector.xml │ │ ├── ic_toolbox_page_start_disabled.xml │ │ ├── ic_toolbox_page_start_enabled.xml │ │ ├── ic_toolbox_page_start_selector.xml │ │ ├── ic_toolbox_pencil_normal.xml │ │ ├── ic_toolbox_pencil_selected.xml │ │ ├── ic_toolbox_pencil_selector.xml │ │ ├── ic_toolbox_prevpage_disabled.xml │ │ ├── ic_toolbox_prevpage_enabled.xml │ │ ├── ic_toolbox_prevpage_selector.xml │ │ ├── ic_toolbox_rectangle_normal.xml │ │ ├── ic_toolbox_rectangle_selected.xml │ │ ├── ic_toolbox_rectangle_selector.xml │ │ ├── ic_toolbox_redo_disabled.xml │ │ ├── ic_toolbox_redo_enabled.xml │ │ ├── ic_toolbox_redo_pressed.xml │ │ ├── ic_toolbox_redo_selector.xml │ │ ├── ic_toolbox_reset_normal.xml │ │ ├── ic_toolbox_reset_pressed.xml │ │ ├── ic_toolbox_reset_selected.xml │ │ ├── ic_toolbox_reset_selector.xml │ │ ├── ic_toolbox_scene_add.xml │ │ ├── ic_toolbox_scene_delete.xml │ │ ├── ic_toolbox_scene_item_id_bg_normal.xml │ │ ├── ic_toolbox_scene_item_id_bg_selected.xml │ │ ├── ic_toolbox_scene_item_id_bg_selector.xml │ │ ├── ic_toolbox_scenes_normal.xml │ │ ├── ic_toolbox_scenes_pressed.xml │ │ ├── ic_toolbox_scenes_selected.xml │ │ ├── ic_toolbox_scenes_selector.xml │ │ ├── ic_toolbox_selector_normal.xml │ │ ├── ic_toolbox_selector_selected.xml │ │ ├── ic_toolbox_selector_selector.xml │ │ ├── ic_toolbox_text_normal.xml │ │ ├── ic_toolbox_text_selected.xml │ │ ├── ic_toolbox_text_selector.xml │ │ ├── ic_toolbox_undo_disabled.xml │ │ ├── ic_toolbox_undo_enabled.xml │ │ ├── ic_toolbox_undo_pressed.xml │ │ ├── ic_toolbox_undo_selector.xml │ │ ├── ic_user_profile_avater.xml │ │ ├── ic_user_profile_email.xml │ │ ├── ic_user_profile_github.xml │ │ ├── ic_user_profile_google.xml │ │ ├── ic_user_profile_head.xml │ │ ├── ic_user_profile_phone.xml │ │ ├── ic_user_profile_wechat.xml │ │ ├── img_cloud_list_empty_dark.xml │ │ ├── img_cloud_list_empty_light.xml │ │ ├── img_history_list_empty_dark.xml │ │ ├── img_history_list_empty_light.xml │ │ ├── img_pad_home_ext_empty.xml │ │ ├── img_room_list_empty_dark.xml │ │ ├── img_room_list_empty_light.xml │ │ ├── img_user_left.xml │ │ ├── launcher_bg.xml │ │ ├── playback_seek_bar_drawable.xml │ │ ├── playback_thumb_shape.xml │ │ ├── selector_item_accept_handup_bg.xml │ │ ├── selector_item_window_app_bg.xml │ │ ├── shape_black50_round_16_bg.xml │ │ ├── shape_black_solid_round_16_bg.xml │ │ ├── shape_blue_border_4_round_8_bg.xml │ │ ├── shape_blue_border_round_4_bg.xml │ │ ├── shape_blue_solid_round_16_bg.xml │ │ ├── shape_blue_solid_round_4_bg.xml │ │ ├── shape_dialog_bg.xml │ │ ├── shape_gray_border_left_round_bg.xml │ │ ├── shape_gray_border_right_round_bg.xml │ │ ├── shape_gray_border_round_16_bg.xml │ │ ├── shape_gray_border_round_40_bg.xml │ │ ├── shape_gray_border_round_4_bg.xml │ │ ├── shape_gray_border_round_8_bg.xml │ │ ├── shape_gray_light_solid_round_12_bg.xml │ │ ├── shape_gray_solid_round_12_bg.xml │ │ ├── shape_red_border_round_4_bg.xml │ │ ├── shape_user_window_border.xml │ │ ├── switch_custom_thumb_selector.xml │ │ └── switch_custom_track_selector.xml │ │ ├── layout-land │ │ └── camera_ui_container.xml │ │ ├── layout │ │ ├── activity_camera.xml │ │ ├── activity_replay.xml │ │ ├── activity_room_play.xml │ │ ├── camera_ui_container.xml │ │ ├── component_cloud.xml │ │ ├── component_extension.xml │ │ ├── component_fullscreen.xml │ │ ├── component_message.xml │ │ ├── component_replay_tool.xml │ │ ├── component_replay_video.xml │ │ ├── component_replay_whiteboard.xml │ │ ├── component_room_state.xml │ │ ├── component_tool.xml │ │ ├── component_user_windows.xml │ │ ├── component_video_list.xml │ │ ├── component_whiteboard.xml │ │ ├── dialog_invite.xml │ │ ├── dialog_loading.xml │ │ ├── dialog_owner_exit.xml │ │ ├── dialog_replay_exit.xml │ │ ├── dialog_request_device.xml │ │ ├── dialog_room_exit.xml │ │ ├── fragment_camera.xml │ │ ├── fragment_gallery.xml │ │ ├── item_class_rtc_video.xml │ │ ├── item_room_accept_handup.xml │ │ ├── item_room_cloud_storage.xml │ │ ├── item_room_message.xml │ │ ├── item_room_user_list.xml │ │ ├── item_scene_preview.xml │ │ ├── item_toolbox_appliance.xml │ │ ├── item_window_app.xml │ │ ├── layout_flat_controller_group.xml │ │ ├── layout_flat_tools_additions.xml │ │ ├── layout_room_accept_handup.xml │ │ ├── layout_room_cloud_storage.xml │ │ ├── layout_room_message_list.xml │ │ ├── layout_room_settings.xml │ │ ├── layout_room_user_list.xml │ │ ├── layout_time_state.xml │ │ ├── layout_user_window.xml │ │ ├── layout_window_apps.xml │ │ ├── recycler_item_footer.xml │ │ └── replay_video_item.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ └── nav_graph.xml │ │ ├── raw │ │ ├── loading_dark.gif │ │ └── loading_light.gif │ │ ├── values-night │ │ ├── colors.xml │ │ └── themes.xml │ │ ├── values-sw600dp │ │ ├── attrs.xml │ │ └── dimens.xml │ │ ├── values-v28 │ │ └── themes.xml │ │ ├── values-zh │ │ ├── arrays.xml │ │ └── strings.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── colors_chart.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ │ └── xml │ │ ├── network_security_config.xml │ │ └── provider_paths.xml │ └── test │ └── java │ └── io │ └── agora │ └── flat │ ├── ExampleUnitTest.kt │ ├── common │ ├── board │ │ └── WhiteSyncedStateTest.kt │ ├── rtc │ │ └── AgoraRtcTest.kt │ ├── rtm │ │ └── ClassRtmEventTest.kt │ └── version │ │ └── VersionCheckerTest.kt │ ├── data │ ├── RoomServiceFetcherTest.kt │ └── model │ │ └── WeekTest.kt │ ├── ui │ └── manager │ │ ├── RecordManagerTest.kt │ │ └── WindowsDragManagerTest.kt │ └── util │ ├── FlatExtensionsKtTest.kt │ └── FlatKotlinTest.kt ├── art ├── flat-logo.png ├── flat-showcase-zh.jpg └── flat-showcase.png ├── base ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── io │ └── agora │ └── flat │ └── di │ └── interfaces │ ├── Crashlytics.kt │ ├── LogConfig.kt │ ├── LogReporter.kt │ ├── Logger.kt │ ├── PostLoginInitializer.kt │ └── StartupInitializer.kt ├── build.gradle ├── flatrun.keystore ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── logger ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── io │ └── agora │ └── flat │ └── logger │ ├── AliyunLogReporter.kt │ ├── BuglyCrashlytics.kt │ ├── FlatLogger.kt │ ├── LoggerModule.kt │ └── TimberInitializer.kt ├── script ├── activity │ ├── TemplateActivity.kt │ ├── TemplateUiAction.kt │ ├── TemplateUiViewState.kt │ └── TemplateViewModel.kt ├── create_new_activity.sh └── update_board.sh └── settings.gradle /.github/ci-gradle.properties.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/.github/ci-gradle.properties.gpg -------------------------------------------------------------------------------- /.github/flat.keystore.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/.github/flat.keystore.gpg -------------------------------------------------------------------------------- /.github/google-services.json.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/.github/google-services.json.gpg -------------------------------------------------------------------------------- /.github/scripts/decrypt_secret.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir $HOME/flat 4 | # decrpyt keystore 5 | gpg --quiet --batch --yes --decrypt --passphrase="$KEYSTORE_SECRET_PASSPHRASE" \ 6 | --output $GITHUB_WORKSPACE/flat.keystore $GITHUB_WORKSPACE/.github/flat.keystore.gpg 7 | # decrpyt gradle properties 8 | gpg --quiet --batch --yes --decrypt --passphrase="$GRADLE_SECRET_PASSPHRASE" \ 9 | --output $HOME/flat/ci-gradle.properties $GITHUB_WORKSPACE/.github/ci-gradle.properties.gpg 10 | # decrpyt google-services.json 11 | gpg --quiet --batch --yes --decrypt --passphrase="$GRADLE_SECRET_PASSPHRASE" \ 12 | --output $GITHUB_WORKSPACE/app/google-services.json $GITHUB_WORKSPACE/.github/google-services.json.gpg 13 | 14 | mkdir -p ~/.gradle 15 | cp $HOME/flat/ci-gradle.properties ~/.gradle/gradle.properties 16 | -------------------------------------------------------------------------------- /.github/scripts/project_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sed -i "s/ALIYUN_LOG_DEV_AK_VALUE/$ALIYUN_LOG_DEV_AK/g" "$GITHUB_WORKSPACE/app/build.gradle" 4 | sed -i "s/ALIYUN_LOG_DEV_SK_VALUE/$ALIYUN_LOG_DEV_SK/g" "$GITHUB_WORKSPACE/app/build.gradle" 5 | sed -i "s/ALIYUN_LOG_PROD_AK_VALUE/$ALIYUN_LOG_PROD_AK/g" "$GITHUB_WORKSPACE/app/build.gradle" 6 | sed -i "s/ALIYUN_LOG_PROD_SK_VALUE/$ALIYUN_LOG_PROD_SK/g" "$GITHUB_WORKSPACE/app/build.gradle" 7 | 8 | sed -i "s/ALIYUN_LOG_SG_PROD_AK_VALUE/$ALIYUN_LOG_SG_PROD_AK/g" "$GITHUB_WORKSPACE/app/build.gradle" 9 | sed -i "s/ALIYUN_LOG_SG_PROD_SK_VALUE/$ALIYUN_LOG_SG_PROD_SK/g" "$GITHUB_WORKSPACE/app/build.gradle" 10 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/agora/flat/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("io.agora.flat", appContext.packageName) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/flat/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/flint/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/flint/res/drawable-xxhdpi/ic_flat_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/drawable-xxhdpi/ic_flat_logo.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/flint/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/flint/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/assets/flatboard/b280a53: -------------------------------------------------------------------------------- 1 | here are what injected into build 2 | @netless/app-countdown@0.0.7 3 | @netless/app-dice@0.1.1 4 | @netless/app-geogebra@0.0.6 5 | @netless/app-iframe-bridge@0.0.2 6 | @netless/app-mindmap@0.1.1 7 | @netless/app-monaco@0.2.0-canary.1 8 | @netless/app-quill@0.1.1 9 | @netless/app-selector@0.0.3 10 | -------------------------------------------------------------------------------- /app/src/main/assets/flatboard/e33d6c2: -------------------------------------------------------------------------------- 1 | here are what injected into build 2 | @netless/app-countdown@0.0.7 3 | @netless/app-dice@0.1.1 4 | @netless/app-geogebra@0.0.6 5 | @netless/app-iframe-bridge@0.0.2 6 | @netless/app-mindmap@0.1.1 7 | @netless/app-monaco@0.2.0-canary.1 8 | @netless/app-quill@0.1.1 9 | @netless/app-selector@0.0.3 10 | -------------------------------------------------------------------------------- /app/src/main/assets/flatboard/e7b0b24: -------------------------------------------------------------------------------- 1 | here are what injected into build 2 | @netless/app-countdown@0.0.7 3 | @netless/app-dice@0.1.1 4 | @netless/app-geogebra@0.0.6 5 | @netless/app-iframe-bridge@0.0.2 6 | @netless/app-mindmap@0.1.1 7 | @netless/app-monaco@0.2.0-canary.1 8 | @netless/app-quill@0.1.1 9 | @netless/app-selector@0.0.3 10 | -------------------------------------------------------------------------------- /app/src/main/assets/flatboard/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | white-sdk-bridge Application 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/assets/lottie/images/img_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/assets/lottie/images/img_0.png -------------------------------------------------------------------------------- /app/src/main/assets/lottie/images/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/assets/lottie/images/img_1.png -------------------------------------------------------------------------------- /app/src/main/assets/reward.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/assets/reward.mp3 -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/AppInitializers.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat 2 | 3 | import android.content.Context 4 | import dagger.hilt.android.qualifiers.ApplicationContext 5 | import io.agora.flat.di.interfaces.StartupInitializer 6 | import javax.inject.Inject 7 | import javax.inject.Singleton 8 | 9 | @Singleton 10 | class AppInitializers @Inject constructor( 11 | @ApplicationContext val context: Context, 12 | private val initializers: Set<@JvmSuppressWildcards StartupInitializer> 13 | ) { 14 | private var inited = false 15 | 16 | fun init() { 17 | if (inited) return 18 | initializers.forEach { 19 | it.init(context) 20 | } 21 | inited = true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/Config.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat 2 | 3 | class Config { 4 | companion object { 5 | // three days in milliseconds 6 | const val INTERVAL_VERSION_CHECK = 259_200_000L 7 | 8 | // ten minutes in milliseconds 9 | var callVersionCheckInterval = 10 * 60_000L 10 | 11 | val cancelAccountCountTime = if (BuildConfig.DEBUG) 3_000L else 30_000L 12 | 13 | const val defaultBoardRatio = 9f / 16 14 | 15 | const val defaultWindowScale = 0.4f 16 | 17 | const val defaultMinWindowScale = 0.25f 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/PostLoginInitializers.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat 2 | 3 | import android.content.Context 4 | import dagger.hilt.android.qualifiers.ApplicationContext 5 | import io.agora.flat.di.interfaces.PostLoginInitializer 6 | import io.agora.flat.di.interfaces.StartupInitializer 7 | import javax.inject.Inject 8 | import javax.inject.Singleton 9 | 10 | @Singleton 11 | class PostLoginInitializers @Inject constructor( 12 | @ApplicationContext val context: Context, 13 | private val initializers: Set<@JvmSuppressWildcards PostLoginInitializer>, 14 | ) { 15 | fun init() { 16 | initializers.forEach { 17 | it.init(context) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/common/android/WindowFocusObserver.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.android 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.LaunchedEffect 5 | import androidx.compose.runtime.rememberUpdatedState 6 | import androidx.compose.runtime.snapshotFlow 7 | import androidx.compose.ui.platform.LocalWindowInfo 8 | 9 | @Composable 10 | fun WindowFocusObserver(onWindowFocusChanged: (isWindowFocused: Boolean) -> Unit) { 11 | val windowInfo = LocalWindowInfo.current 12 | val callback = rememberUpdatedState(onWindowFocusChanged) 13 | LaunchedEffect(windowInfo) { 14 | snapshotFlow { windowInfo.isWindowFocused }.collect { callback.value(it) } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/common/board/BoardEntity.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.board 2 | 3 | sealed class BoardPhase { 4 | object Init : BoardPhase() 5 | object Connecting : BoardPhase() 6 | object Connected : BoardPhase() 7 | object Disconnected : BoardPhase() 8 | data class Error(val message: String) : BoardPhase() 9 | } 10 | 11 | sealed class BoardError { 12 | object Kicked : BoardError() 13 | data class Unknown(val message: String) : BoardError() 14 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/common/board/StorageState.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.board 2 | 3 | data class DeviceState( 4 | val camera: Boolean, 5 | val mic: Boolean, 6 | ) 7 | 8 | data class ClassroomState( 9 | val raiseHandUsers: List = listOf(), 10 | val ban: Boolean = false 11 | ) 12 | 13 | data class UserWindows( 14 | val grid: List = listOf(), 15 | val users: Map = mapOf() 16 | ) 17 | 18 | data class WindowInfo( 19 | val x: Float, 20 | val y: Float, 21 | val z: Int, 22 | val width: Float, 23 | val height: Float, 24 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/common/login/LoginModels.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.login 2 | 3 | import io.agora.flat.ui.util.UiMessage 4 | 5 | enum class LoginType { 6 | None, 7 | WeChat, 8 | Github, 9 | Google, 10 | } 11 | 12 | sealed class LoginState { 13 | object Init : LoginState() 14 | data class Process(val message: UiMessage) : LoginState() 15 | object Success : LoginState() 16 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/common/rtc/RtcEvent.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.rtc 2 | 3 | data class AudioVolumeInfo( 4 | val uid: Int, 5 | val volume: Int, 6 | val vad: Int, 7 | ) 8 | 9 | 10 | enum class NetworkQuality { 11 | Excellent, 12 | Good, 13 | Bad, 14 | Unknown 15 | } 16 | 17 | sealed class RtcEvent { 18 | data class UserOffline(val uid: Int, val reason: Int) : RtcEvent() 19 | 20 | data class UserJoined(val uid: Int, val elapsed: Int) : RtcEvent() 21 | 22 | class VolumeIndication(val speakers: List, val totalVolume: Int) : RtcEvent() 23 | 24 | class NetworkStatus(val quality: NetworkQuality) : RtcEvent() 25 | 26 | class LastmileDelay(val delay: Int) : RtcEvent() 27 | 28 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/common/rtc/RtcJoinOptions.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.rtc 2 | 3 | data class RtcJoinOptions( 4 | val token: String, 5 | val channel: String, 6 | val uid: Int, 7 | 8 | val audioOpen: Boolean = false, 9 | val videoOpen: Boolean = false, 10 | ) 11 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/common/rtm/Misc.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.rtm 2 | 3 | internal interface RtmListener { 4 | fun onClassEvent(event: ClassRtmEvent) 5 | 6 | fun onChatMessage(chatMessage: ChatMessage) 7 | 8 | fun onMemberJoined(userId: String, channelId: String) 9 | 10 | fun onMemberLeft(userId: String, channelId: String) 11 | 12 | fun onRemoteLogin() {} 13 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/common/rtm/RtmMember.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.rtm 2 | 3 | data class RtmMember( 4 | val userId: String, 5 | val channelId: String, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/common/version/VersionCheckResult.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.version 2 | 3 | data class VersionCheckResult( 4 | val appUrl: String? = null, 5 | val appVersion: String? = null, 6 | val title: String = "", 7 | val description: String = "", 8 | val gotoMarket: Boolean = false, 9 | val showUpdate: Boolean = false, 10 | val forceUpdate: Boolean = false, 11 | ) { 12 | companion object { 13 | val Empty = VersionCheckResult() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data 2 | 3 | import androidx.room.AutoMigration 4 | import androidx.room.Database 5 | import androidx.room.RoomDatabase 6 | import io.agora.flat.data.dao.RecordHistoryDao 7 | import io.agora.flat.data.dao.RoomConfigDao 8 | import io.agora.flat.data.model.RecordHistory 9 | import io.agora.flat.data.model.RoomConfig 10 | 11 | @Database( 12 | entities = [RoomConfig::class, RecordHistory::class], 13 | version = 2, 14 | autoMigrations = [AutoMigration(from = 1, to = 2)] 15 | ) 16 | abstract class AppDatabase : RoomDatabase() { 17 | abstract fun roomConfigDao(): RoomConfigDao 18 | 19 | abstract fun recordHistoryDao(): RecordHistoryDao 20 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/dao/RoomConfigDao.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.dao 2 | 3 | import androidx.room.* 4 | import io.agora.flat.data.model.RoomConfig 5 | 6 | @Dao 7 | interface RoomConfigDao { 8 | @Query("SELECT * FROM room_config") 9 | fun getAll(): List 10 | 11 | @Query("SELECT * FROM room_config WHERE uuid is :uuid") 12 | fun getConfigById(uuid: String): RoomConfig? 13 | 14 | @Insert 15 | fun insert(roomConfig: RoomConfig) 16 | 17 | @Update 18 | fun update(roomConfig: RoomConfig) 19 | 20 | @Delete 21 | fun delete(roomConfig: RoomConfig) 22 | 23 | fun insertOrUpdate(roomConfig: RoomConfig) { 24 | val existConfig = getConfigById(roomConfig.uuid) 25 | if (existConfig != null) { 26 | update(roomConfig) 27 | } else { 28 | insert(roomConfig) 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/AgreementsResp.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class AgreementsResp( 4 | val data: Map> 5 | ) 6 | 7 | data class Agreement( 8 | val title: String, 9 | val message: String, 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/AuthUUIDReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class AuthUUIDReq constructor( 4 | // for csrf 5 | val authUUID: String, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/AvatarData.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class AvatarData(val avatarURL: String) 4 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/BaseReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | class BaseReq { 4 | companion object { 5 | val EMPTY = BaseReq() 6 | } 7 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/BaseResp.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class BaseResp( 6 | @SerializedName("status") val status: Int, 7 | @SerializedName("code") val code: Int?, 8 | @SerializedName("data") val data: T, 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/CallingCode.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import android.os.Parcelable 4 | import kotlinx.parcelize.Parcelize 5 | 6 | @Parcelize 7 | data class Country( 8 | // country name 9 | val name: String, 10 | // calling code 11 | val cc: String, 12 | // country code 13 | val code: String, 14 | ) : Parcelable 15 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/CancelRoomReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class CancelRoomReq constructor( 4 | val roomUUID: String? = null, 5 | val periodicUUID: String? = null, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/ClassModeType.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | enum class ClassModeType { 4 | Lecture, 5 | Interaction; 6 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/CloudTypes.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | enum class FileConvertStep { 6 | None, 7 | Converting, 8 | Done, 9 | Failed; 10 | } 11 | 12 | enum class ResourceType { 13 | @SerializedName("WhiteboardConvert") 14 | WhiteboardConvert, 15 | 16 | @SerializedName("WhiteboardProjector") 17 | WhiteboardProjector, 18 | 19 | @SerializedName("NormalResources") 20 | NormalResources, 21 | 22 | @SerializedName("Directory") 23 | Directory, 24 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/CoursewareType.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | enum class CoursewareType { 4 | Unknown, 5 | Image, 6 | Audio, 7 | Video, 8 | DocStatic, 9 | DocDynamic, 10 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/DocsType.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | enum class DocsType { 4 | Dynamic, 5 | Static, 6 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/EmailBindReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class EmailBindReq( 4 | val email: String, 5 | val code: String, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/EmailCodeReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class EmailCodeReq( 4 | val email: String, 5 | val language: String, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/EmailPasswordReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class EmailPasswordReq( 4 | val email: String, 5 | // 8..32 length 6 | val password: String, 7 | ) 8 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/EmailRegisterReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class EmailRegisterReq( 4 | val email: String, 5 | val code: String, 6 | // 8..32 length 7 | val password: String, 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/EmailReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class EmailReq( 4 | val email: String, 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/InviteInfo.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class InviteInfo( 4 | val username: String, 5 | val roomTitle: String, 6 | val link: String, 7 | val roomUuid: String, 8 | val beginTime: Long, 9 | val endTime: Long, 10 | val isPmi: Boolean 11 | ) 12 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/JoinRoomRecord.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class JoinRoomRecordList( 4 | val items: List = emptyList() 5 | ) 6 | 7 | data class JoinRoomRecord( 8 | val title: String, 9 | val uuid: String 10 | ) 11 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/JoinRoomReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import io.agora.flat.BuildConfig 4 | 5 | data class JoinRoomReq(val uuid: String, val version: String = BuildConfig.VERSION_NAME) 6 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/LoadState.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | sealed class LoadState { 4 | object Loading : LoadState() 5 | data class NotLoading(val end: Boolean) : LoadState() { 6 | companion object { 7 | val Complete = NotLoading(end = true) 8 | val Incomplete = NotLoading(end = false) 9 | } 10 | } 11 | 12 | data class Error(val error: Throwable) : LoadState() 13 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/LoginCheckReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class LoginCheckReq constructor( 4 | val type: String = "mobile", 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/LoginHistory.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class LoginHistory( 4 | val items: List = emptyList() 5 | ) 6 | 7 | data class LoginHistoryItem( 8 | val value: String, 9 | val password: String, 10 | ) 11 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/LoginPlatform.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | enum class LoginPlatform { 4 | WeChat, 5 | Github, 6 | Apple, 7 | Google, 8 | Email, 9 | Phone, 10 | // Agora, 11 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/MessageCountResp.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class MessageCountResp( 4 | val result: String, 5 | val code: String, 6 | val count: Int, 7 | ) 8 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/MessageListResp.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class MessageListResp( 4 | val result: String, 5 | val code: String, 6 | val messages: List, 7 | val request_id: String, 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/MessageQueryHistoryReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | const val ORDER_ASC = "asc" 4 | const val ORDER_DESC = "desc" 5 | 6 | data class MessageQueryHistoryReq( 7 | val filter: MessageQueryFilter, 8 | val offset: Int = 0, 9 | val limit: Int = 100, 10 | val order: String = ORDER_ASC, 11 | ) 12 | 13 | data class MessageQueryFilter( 14 | val source: String? = null, 15 | val destination: String? = null, 16 | val start_time: String, 17 | val end_time: String, 18 | ) 19 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/MessageQueryHistoryResp.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class MessageQueryHistoryResp( 4 | val result: String, 5 | val offset: Int, 6 | val limit: Int, 7 | val order: String, 8 | val location: String, 9 | ) 10 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/NetworkRoomUser.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class NetworkRoomUser( 4 | var userUUID: String, 5 | var rtcUID: Int, 6 | var name: String, 7 | var avatarURL: String, 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/Order.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | enum class Order { 6 | @SerializedName("ASC") 7 | Asc, 8 | 9 | @SerializedName("DESC") 10 | Desc, 11 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/PeriodicSubRoom.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class PeriodicSubRoom constructor( 4 | val roomInfo: RoomInfo, 5 | val previousPeriodicRoomBeginTime: Long?, 6 | val nextPeriodicRoomEndTime: Long?, 7 | val count: Int, 8 | val docs: List, 9 | ) 10 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/PeriodicSubRoomReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class PeriodicSubRoomReq( 4 | val periodicUUID: String, 5 | val roomUUID: String, 6 | val needOtherRoomTimeInfo: Boolean?, 7 | ) 8 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/PhoneOrEmailInfo.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import io.agora.flat.util.isValidEmail 4 | import io.agora.flat.util.isValidPhone 5 | 6 | data class PhoneOrEmailInfo( 7 | val value: String = "", 8 | val cc: String = "", 9 | val code: String = "", 10 | val password: String = "", 11 | val remainTime: Long = 0, 12 | val phoneFirst: Boolean = true, 13 | ) { 14 | val phone: String 15 | get() = "$cc$value" 16 | 17 | val email: String 18 | get() = value 19 | 20 | val isPhone: Boolean 21 | get() = value.isValidPhone() 22 | 23 | val isValidPhoneOrEmail: Boolean 24 | get() = value.isValidPhone() || value.isValidEmail() 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/PhonePasswordReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class PhonePasswordReq( 4 | val phone: String, 5 | // 8..32 length 6 | val password: String, 7 | ) 8 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/PhoneRegisterReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class PhoneRegisterReq constructor( 4 | val phone: String, 5 | val code: String, 6 | // 8..32 length 7 | val password: String, 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/PhoneReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class PhoneReq constructor( 4 | val phone: String, 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/PhoneSmsCodeReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class PhoneSmsCodeReq constructor( 4 | val phone: String, 5 | val code: String, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/PureRoomReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | /** 4 | * 用于使用只包含 room uuid 的请求 5 | */ 6 | data class PureRoomReq(val roomUUID: String) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/PureToken.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class PureToken(val token: String) 4 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RecordAcquireReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RecordAcquireReq constructor( 4 | val roomUUID: String, 5 | val agoraData: RecordAcquireReqData, 6 | ) 7 | 8 | data class RecordAcquireReqData constructor( 9 | val clientRequest: RecordAcquireReqDataClientRequest, 10 | ) 11 | 12 | data class RecordAcquireReqDataClientRequest( 13 | val resourceExpiredHour: Int, 14 | val scene: Int, 15 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RecordAcquireRespData.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RecordAcquireRespData constructor( 4 | val resourceId: String, 5 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RecordAgoraParams.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** 6 | * agora record params describe 7 | * https://docs.agora.io/cn/cloud-recording/cloud_recording_api_rest 8 | */ 9 | data class AgoraRecordParams constructor( 10 | val resourceid: String, 11 | val mode: AgoraRecordMode, 12 | val sid: String? = null, 13 | ) 14 | 15 | enum class AgoraRecordMode { 16 | @SerializedName("individual") 17 | Individual, 18 | 19 | @SerializedName("mix") 20 | Mix, 21 | 22 | @SerializedName("web") 23 | Web, 24 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RecordHistory.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | 6 | @Entity(tableName = "record_history") 7 | data class RecordHistory( 8 | val roomUuid: String, 9 | @PrimaryKey 10 | val resourceId: String, 11 | val sid: String, 12 | val mode: AgoraRecordMode = AgoraRecordMode.Mix, 13 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RecordInfo.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RecordInfo constructor( 4 | val title: String, 5 | val ownerUUID: String, 6 | val roomType: RoomType, 7 | val region: String, 8 | val whiteboardRoomToken: String, 9 | val whiteboardRoomUUID: String, 10 | val rtmToken: String, 11 | val recordInfo: List, 12 | ) 13 | 14 | data class RecordItem constructor( 15 | val beginTime: Long, 16 | val endTime: Long, 17 | val videoURL: String, 18 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RecordQueryRespData.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RecordQueryRespData constructor( 4 | val sid: String, 5 | val resourceId: String, 6 | val serverResponse: QueryServerResponse, 7 | ) 8 | 9 | data class QueryServerResponse constructor( 10 | val status: Int, 11 | val fileList: String, 12 | // val fileListMode: String, 13 | val sliceStartTime: Long, 14 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RecordReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RecordReq( 4 | val roomUUID: String, 5 | val agoraParams: AgoraRecordParams, 6 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RecordStartRespData.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RecordStartRespData constructor( 4 | val sid: String, 5 | val resourceId: String, 6 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RecordStopRespData.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RecordStopRespData constructor( 4 | val sid: String, 5 | val resourceId: String, 6 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RecordUpdateLayoutReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RecordUpdateLayoutReq constructor( 4 | val roomUUID: String, 5 | val agoraParams: AgoraRecordParams, 6 | val agoraData: AgoraRecordUpdateLayoutData, 7 | ) 8 | 9 | data class AgoraRecordUpdateLayoutData constructor( 10 | val clientRequest: UpdateLayoutClientRequest, 11 | ) 12 | 13 | data class UpdateLayoutClientRequest constructor( 14 | val maxResolutionUid: String? = null, 15 | val mixedVideoLayout: Int = 3, 16 | val backgroundColor: String = "#FFFFFF", 17 | val layoutConfig: List? = null, 18 | val backgroundConfig: List? = null, 19 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RemoveBindingReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RemoveBindingReq( 4 | val target: LoginPlatform, 5 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RespNoData.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | object RespNoData 4 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomConfig.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "room_config") 8 | data class RoomConfig( 9 | @PrimaryKey val uuid: String, 10 | @ColumnInfo(name = "enable_video") val enableVideo: Boolean = false, 11 | @ColumnInfo(name = "enable_audio") val enableAudio: Boolean = false, 12 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomCountResp.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class RoomCount( 6 | @SerializedName("alreadyJoinedRoomCount") 7 | val count: Int, 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomCreateReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RoomCreateReq constructor( 4 | // 房间主题, 最多 50 字 5 | val title: String, 6 | // 上课类型 7 | val type: RoomType, 8 | // 单位ms 9 | val beginTime: Long, 10 | // 如果不传,则默认是 beginTime 后的一个小时) 11 | val endTime: Long? = beginTime + 3600_000, 12 | // 区域标记 13 | val region: String = "cn-hz", 14 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomCreateRespData.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RoomCreateRespData constructor( 4 | val roomUUID: String, 5 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomDetailOrdinary.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RoomDetailOrdinary constructor( 4 | val roomInfo: RoomInfo, 5 | val docs: ArrayList, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomDetailOrdinaryReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RoomDetailOrdinaryReq constructor(val roomUUID: String, val needDocs: Boolean? = null) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomDetailPeriodic.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | /** 4 | * 周期性房间详情 5 | */ 6 | data class RoomDetailPeriodic constructor( 7 | val periodic: RoomPeriodic, 8 | val rooms: ArrayList, 9 | ) 10 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomDetailPeriodicReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RoomDetailPeriodicReq constructor( 4 | val periodicUUID: String, 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomDocs.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RoomDocs constructor( 4 | val docType: DocsType, 5 | val docUUID: String, 6 | val isPreload: Boolean, 7 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomPeriodic.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RoomPeriodic constructor( 4 | // 创建者的 uuid 5 | val ownerUUID: String, 6 | // 房间类型 7 | val roomType: RoomType, 8 | // 结束时间 9 | val endTime: Long, 10 | // 为 null 时(即 用户选择的是 endTime) 11 | val rate: Long?, 12 | val title: String, 13 | val weeks: List, 14 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomStatus.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | enum class RoomStatus { 4 | // 未上课 5 | Idle, 6 | 7 | // 上课状态 8 | Started, 9 | 10 | // 暂停上课 11 | Paused, 12 | 13 | // 结束上课 14 | Stopped, 15 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomType.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | enum class RoomType { 4 | OneToOne, 5 | SmallClass, 6 | BigClass 7 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomUser.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RoomUser( 4 | val userUUID: String, 5 | val rtcUID: Int = NOT_JOIN_RTC_UID, 6 | val name: String? = null, 7 | val avatarURL: String = "", 8 | 9 | /** 10 | * 上台标识,Owner 用户该标记为 true 11 | */ 12 | val isOnStage: Boolean = false, 13 | val audioOpen: Boolean = false, 14 | val videoOpen: Boolean = false, 15 | /** 16 | * 举手中标识 17 | */ 18 | val isRaiseHand: Boolean = false, 19 | 20 | val isOwner: Boolean = false, 21 | /** 22 | * 白板权限标识,Owner 用户该标记为 true 23 | */ 24 | val allowDraw: Boolean = false, 25 | ) { 26 | val isJoined: Boolean 27 | get() = rtcUID > 0 28 | 29 | companion object { 30 | const val NOT_JOIN_RTC_UID = 0 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RoomUsersReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RoomUsersReq(val roomUUID: String, val usersUUID: List?) 4 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RtmCensorReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RtmCensorReq(val text: String) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RtmCensorRespData.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RtmCensorRespData constructor( 4 | val valid: Boolean, 5 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/RtmQueryMessage.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class RtmQueryMessage( 4 | val dst: String, 5 | val message_type: String, 6 | val ms: Long, 7 | val payload: String, 8 | val src: String, 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/SetPasswordReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class SetPasswordReq constructor( 4 | val password: String? = null, 5 | val newPassword: String, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/StreamAgreement.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class StreamAgreement(val isAgree: Boolean) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/StreamAgreementReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | class StreamAgreementReq(val isAgree: Boolean) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/UserBindings.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class UserBindings( 4 | val wechat: Boolean = false, 5 | val phone: Boolean = false, 6 | val email: Boolean = false, 7 | val agora: Boolean = false, 8 | val apple: Boolean = false, 9 | val github: Boolean = false, 10 | val google: Boolean = false, 11 | 12 | val meta: Meta, 13 | ) { 14 | fun bindingCount(): Int { 15 | return listOf(wechat, phone, email, agora, apple, github, google).count { it } 16 | } 17 | } 18 | 19 | data class Meta( 20 | val wechat: String = "", 21 | val phone: String = "", 22 | val apple: String = "", 23 | val agora: String = "", 24 | val github: String = "", 25 | val email: String = "", 26 | val google: String = "", 27 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/UserInfo.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class UserInfo( 6 | val name: String, 7 | val avatar: String, 8 | @SerializedName("userUUID") val uuid: String, 9 | val hasPhone: Boolean = false, 10 | val hasPassword: Boolean = false, 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/UserInfoRebind.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class UserInfoRebind( 6 | val name: String, 7 | val avatar: String, 8 | @SerializedName("userUUID") val uuid: String, 9 | val token: String, 10 | val hasPhone: Boolean, 11 | val hasPassword: Boolean, 12 | val rebind: RebindExt, 13 | ) { 14 | fun toUserInfo(): UserInfo { 15 | return UserInfo( 16 | name = name, 17 | avatar = avatar, 18 | uuid = uuid, 19 | hasPhone = hasPhone, 20 | hasPassword = hasPassword 21 | ) 22 | } 23 | } 24 | 25 | data class RebindExt( 26 | val agora: Int, 27 | val apple: Int, 28 | val github: Int, 29 | val google: Int, 30 | val wechat: Int, 31 | val email: Int 32 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/UserInfoWithToken.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class UserInfoWithToken( 6 | val name: String, 7 | val avatar: String, 8 | @SerializedName("userUUID") val uuid: String, 9 | val token: String, 10 | val hasPhone: Boolean, 11 | val hasPassword: Boolean, 12 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/UserRenameReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class UserRenameReq constructor( 4 | val name: String, 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/UserTokenData.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class UserTokenData( 4 | val token: String, 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/WeChatCallbackReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | data class WeChatCallbackReq constructor( 4 | val state: String, 5 | val code: String, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/model/Week.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | enum class Week { 6 | @SerializedName("0") 7 | Sunday, 8 | 9 | @SerializedName("1") 10 | Monday, 11 | 12 | @SerializedName("2") 13 | Tuesday, 14 | 15 | @SerializedName("3") 16 | Wednesday, 17 | 18 | @SerializedName("4") 19 | Thursday, 20 | 21 | @SerializedName("5") 22 | Friday, 23 | 24 | @SerializedName("6") 25 | Saturday, 26 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/data/repository/RoomConfigRepository.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.data.repository 2 | 3 | import io.agora.flat.data.dao.RoomConfigDao 4 | import io.agora.flat.data.model.RoomConfig 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlinx.coroutines.withContext 7 | import javax.inject.Inject 8 | import javax.inject.Singleton 9 | 10 | @Singleton 11 | class RoomConfigRepository @Inject constructor( 12 | private val roomConfigDao: RoomConfigDao, 13 | ) { 14 | 15 | suspend fun updateRoomConfig(roomConfig: RoomConfig) { 16 | return withContext(Dispatchers.IO) { 17 | roomConfigDao.insertOrUpdate(roomConfig) 18 | } 19 | } 20 | 21 | suspend fun getRoomConfig(uuid: String): RoomConfig? { 22 | return withContext(Dispatchers.IO) { 23 | roomConfigDao.getConfigById(uuid) 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/di/ActivityRetainedModuleBinds.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.di 2 | 3 | import dagger.Binds 4 | import dagger.Module 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.android.components.ActivityRetainedComponent 7 | import io.agora.flat.common.board.AgoraBoardRoom 8 | import io.agora.flat.common.board.WhiteSyncedState 9 | import io.agora.flat.di.interfaces.BoardRoom 10 | import io.agora.flat.di.interfaces.SyncedClassState 11 | 12 | @Module 13 | @InstallIn(ActivityRetainedComponent::class) 14 | abstract class ActivityRetainedModuleBinds { 15 | @Binds 16 | abstract fun providerBoardRoom(bind: AgoraBoardRoom): BoardRoom 17 | 18 | @Binds 19 | abstract fun providerSyncedState(bind: WhiteSyncedState): SyncedClassState 20 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/di/interfaces/RtcApi.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.di.interfaces 2 | 3 | import io.agora.flat.common.rtc.RtcEvent 4 | import io.agora.flat.common.rtc.RtcJoinOptions 5 | import io.agora.rtc2.video.VideoCanvas 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | interface RtcApi { 9 | fun joinChannel(options: RtcJoinOptions): Int 10 | 11 | fun leaveChannel(): Int 12 | 13 | fun enableLocalVideo(enabled: Boolean) 14 | 15 | fun enableLocalAudio(enabled: Boolean) 16 | 17 | fun setupLocalVideo(local: VideoCanvas) 18 | 19 | fun setupRemoteVideo(remote: VideoCanvas) 20 | 21 | /** 22 | * enable local audio or video 23 | */ 24 | fun updateLocalStream(audio: Boolean, video: Boolean) 25 | 26 | fun updateRemoteStream(rtcUid: Int, audio: Boolean, video: Boolean) 27 | 28 | fun observeRtcEvent(): Flow 29 | 30 | companion object { 31 | const val MAX_CAPACITY = 17 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/event/EventBus.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.event 2 | 3 | import kotlinx.coroutines.flow.MutableSharedFlow 4 | import kotlinx.coroutines.flow.asSharedFlow 5 | 6 | /** 7 | * a event bus to manager app events 8 | */ 9 | class EventBus { 10 | private val _events = MutableSharedFlow() 11 | val events = _events.asSharedFlow() 12 | 13 | suspend fun produceEvent(event: Event) { 14 | _events.emit(event) 15 | } 16 | } 17 | 18 | abstract class Event -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/event/MessagesAppended.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.event 2 | 3 | import io.agora.flat.common.rtm.Message 4 | 5 | data class MessagesAppended constructor(val messages: List) : Event() -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/event/NoOptPermission.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.event 2 | 3 | import java.util.* 4 | 5 | data class NoOptPermission(val id: Long = UUID.randomUUID().mostSignificantBits) : Event() 6 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/event/RoomsUpdated.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.event 2 | 3 | object RoomsUpdated : Event() -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/event/UserBindingsUpdated.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.event 2 | 3 | import java.util.* 4 | 5 | /** 6 | * notify this event when account user info updated 7 | * workaround for observe [io.agora.flat.data.repository.UserRepository] 8 | */ 9 | data class UserBindingsUpdated(val id: Long = UUID.randomUUID().mostSignificantBits) : Event() -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/event/UserUpdated.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.event 2 | 3 | /** 4 | * notify this event when account user info updated 5 | * workaround for observe [io.agora.flat.data.repository.UserRepository] 6 | */ 7 | object UserUpdated : Event() -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/HeaderProvider.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http 2 | 3 | /** 4 | * 通用请求头处理,支持多模块实现 5 | */ 6 | interface HeaderProvider { 7 | fun getHeaders(): Set> 8 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/interceptor/AgoraMessageInterceptor.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.interceptor 2 | 3 | import okhttp3.Interceptor 4 | import okhttp3.Response 5 | 6 | class AgoraMessageInterceptor : Interceptor { 7 | override fun intercept(chain: Interceptor.Chain): Response { 8 | val request = chain.request() 9 | 10 | val builder = request.newBuilder().apply { 11 | addHeader("Content-type", "application/json; charset=utf-8") 12 | } 13 | 14 | return chain.proceed(builder.build()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/interceptor/HeaderInterceptor.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.interceptor 2 | 3 | import io.agora.flat.http.HeaderProvider 4 | import okhttp3.Interceptor 5 | import okhttp3.Response 6 | 7 | class HeaderInterceptor constructor(private var headerProviders: Set) : Interceptor { 8 | 9 | override fun intercept(chain: Interceptor.Chain): Response { 10 | val request = chain.request() 11 | 12 | val builder = request.newBuilder().apply { 13 | addHeader("Content-type", "application/json; charset=utf-8") 14 | 15 | for (headerProvider in headerProviders) { 16 | for (pair in headerProvider.getHeaders()) { 17 | addHeader(pair.first, pair.second) 18 | } 19 | } 20 | } 21 | 22 | return chain.proceed(builder.build()) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/interceptor/OtherInterceptor.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.interceptor 2 | 3 | import okhttp3.Interceptor 4 | import okhttp3.Response 5 | 6 | class OtherInterceptor : Interceptor { 7 | override fun intercept(chain: Interceptor.Chain): Response { 8 | return chain.proceed(chain.request()) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudConvertFinishReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CloudConvertFinishReq constructor( 4 | val fileUUID: String, 5 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudConvertStartReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CloudConvertStartReq constructor( 4 | val fileUUID: String, 5 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudConvertStartResp.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | import io.agora.flat.data.model.ResourceType 4 | 5 | data class CloudConvertStartResp constructor( 6 | val resourceType: ResourceType, 7 | val whiteboardConvert: WhiteboardConvert?, 8 | val whiteboardProjector: WhiteboardProjector?, 9 | ) 10 | 11 | data class WhiteboardConvert constructor( 12 | val taskUUID: String, 13 | val taskToken: String, 14 | ) 15 | 16 | data class WhiteboardProjector constructor( 17 | val taskUUID: String, 18 | val taskToken: String, 19 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudFileDeleteReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CloudFileDeleteReq( 4 | val uuids: List, 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudFileMoveReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CloudFileMoveReq( 4 | val uuids: List, 5 | val targetDirectoryPath: String, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudFileRenameReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CloudFileRenameReq( 4 | val fileUUID: String, 5 | val newName: String, 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudListFilesReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CloudListFilesReq( 4 | val page: Int, 5 | val directoryPath: String, 6 | val size: Int, 7 | val order: String, 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudListFilesResp.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | import io.agora.flat.data.model.CloudFile 4 | 5 | data class CloudListFilesResp constructor( 6 | val totalUsage: Long, 7 | val files: List, 8 | val canCreateDirectory: Boolean, 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudUploadFinishReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CloudUploadFinishReq constructor( 4 | val fileUUID: String, 5 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudUploadStartReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CloudUploadStartReq constructor( 4 | val fileName: String, 5 | val fileSize: Long, 6 | val targetDirectoryPath: String, 7 | val convertType: String? = null, 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudUploadStartResp.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CloudUploadStartResp constructor( 4 | val fileUUID: String, 5 | val ossDomain: String, 6 | val ossFilePath: String, 7 | val policy: String, 8 | val signature: String, 9 | val convertType: String? = null, 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CloudUploadTempFileStartReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CloudUploadTempFileStartReq constructor( 4 | val fileName: String, 5 | val fileSize: Long, 6 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/http/model/CreateDirectoryReq.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.http.model 2 | 3 | data class CreateDirectoryReq( 4 | val parentDirectoryPath: String, 5 | val directoryName: String, 6 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/base/BaseAccountViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.base 2 | 3 | import androidx.lifecycle.viewModelScope 4 | import io.agora.flat.util.Ticker 5 | import kotlinx.coroutines.Job 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.launch 8 | 9 | abstract class BaseAccountViewModel : BaseViewModel() { 10 | protected val remainTime = MutableStateFlow(0L) 11 | private var countDownJob: Job? = null 12 | 13 | protected fun startCountDown() { 14 | countDownJob?.cancel() 15 | countDownJob = viewModelScope.launch { 16 | Ticker.countDownFlow(60).collect { 17 | remainTime.value = it 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/base/BaseComposeActivity.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.base 2 | 3 | import android.content.pm.ActivityInfo 4 | import io.agora.flat.util.isPhoneMode 5 | 6 | open class BaseComposeActivity : BaseActivity() { 7 | override fun lockOrientation() { 8 | if (isPhoneMode()) { 9 | this.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT 10 | } else { 11 | this.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/base/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.base 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import io.agora.flat.util.Ticker 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.launch 8 | 9 | abstract class BaseViewModel : ViewModel() { 10 | 11 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/cloud/preview/PreviewAction.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.cloud.preview 2 | 3 | sealed class PreviewAction { 4 | object OnClose : PreviewAction() 5 | object OnLoadFinished : PreviewAction() 6 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/cloud/preview/PreviewState.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.cloud.preview 2 | 3 | import io.agora.flat.data.model.CloudFile 4 | import io.agora.flat.data.model.CoursewareType 5 | import io.agora.flat.util.JsonUtils 6 | import java.net.URLEncoder 7 | 8 | data class PreviewState( 9 | val loading: Boolean, 10 | val type: CoursewareType = CoursewareType.Unknown, 11 | val file: CloudFile? = null, 12 | val baseUrl: String? = null, 13 | ) { 14 | // provider by flat web 15 | val previewUrl: String 16 | get() { 17 | if (file == null) return "" 18 | return "$baseUrl/preview/${URLEncoder.encode(JsonUtils.toJson(file), "utf-8")}/" 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/cloud/uploading/UploadingUIState.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.cloud.uploading 2 | 3 | import io.agora.flat.common.upload.UploadFile 4 | 5 | data class UploadingUIState( 6 | val uploadFiles: List = emptyList(), 7 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/history/HistoryUiState.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.history 2 | 3 | import io.agora.flat.data.model.RoomInfo 4 | 5 | data class HistoryUiState( 6 | val histories: List = listOf(), 7 | val refreshing: Boolean = true, 8 | val noMore: Boolean = true, 9 | ) { 10 | companion object { 11 | val Empty = HistoryUiState() 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/home/HomeUiState.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.home 2 | 3 | import io.agora.flat.data.model.RoomInfo 4 | import io.agora.flat.data.model.UserInfo 5 | 6 | data class HomeUiState( 7 | val refreshing: Boolean = false, 8 | val roomList: List = listOf(), 9 | val userInfo: UserInfo = UserInfo("", "", ""), 10 | val networkActive: Boolean = true, 11 | val errorMessage: String? = null, 12 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/login/LoginUiAction.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.login 2 | 3 | import io.agora.flat.data.model.PhoneOrEmailInfo 4 | 5 | 6 | sealed class LoginUiAction { 7 | object WeChatLogin : LoginUiAction() 8 | object GithubLogin : LoginUiAction() 9 | object GoogleLogin : LoginUiAction() 10 | 11 | object OpenServiceProtocol : LoginUiAction() 12 | object OpenPrivacyProtocol : LoginUiAction() 13 | 14 | data class PhoneSendCode(val phone: String) : LoginUiAction() 15 | data class PhoneLogin(val phone: String, val code: String) : LoginUiAction() 16 | 17 | object SignUpClick : LoginUiAction() 18 | 19 | class PasswordLoginClick(val info: PhoneOrEmailInfo) : LoginUiAction() 20 | class LoginInputChange(val info: PhoneOrEmailInfo) : LoginUiAction() 21 | 22 | object ForgotPwdClick : LoginUiAction() 23 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/play/BaseComponent.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.play 2 | 3 | import android.content.res.Configuration 4 | import android.widget.FrameLayout 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.lifecycle.DefaultLifecycleObserver 7 | import androidx.lifecycle.Lifecycle 8 | import androidx.lifecycle.LifecycleOwner 9 | 10 | abstract class BaseComponent( 11 | val activity: AppCompatActivity, 12 | val rootView: FrameLayout, 13 | ) : LifecycleOwner, DefaultLifecycleObserver { 14 | 15 | override fun getLifecycle(): Lifecycle = activity.lifecycle 16 | 17 | open fun onConfigurationChanged(newConfig: Configuration) {} 18 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/play/WindowLocalState.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.play 2 | 3 | import android.graphics.Rect 4 | 5 | data class UserWindowUiState( 6 | val centerX: Float, 7 | val centerY: Float, 8 | val width: Float, 9 | val height: Float, 10 | val index: Int, 11 | ) { 12 | private var rect: Rect? = null 13 | 14 | fun getRect(): Rect { 15 | if (rect == null) rect = Rect() 16 | val rect = rect!! 17 | rect.set( 18 | (centerX - width / 2).toInt(), 19 | (centerY - height / 2).toInt(), 20 | (centerX + width / 2).toInt(), 21 | (centerY + height / 2).toInt(), 22 | ) 23 | return rect 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/room/SubscribeRoomActivity.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.room 2 | 3 | import android.os.Bundle 4 | import androidx.activity.compose.setContent 5 | import androidx.compose.ui.res.stringResource 6 | import io.agora.flat.R 7 | import io.agora.flat.ui.activity.base.BaseComposeActivity 8 | import io.agora.flat.ui.compose.CloseTopAppBar 9 | import io.agora.flat.ui.compose.FlatColumnPage 10 | 11 | class SubscribeRoomActivity : BaseComposeActivity() { 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContent { 15 | FlatColumnPage { 16 | CloseTopAppBar( 17 | stringResource(id = R.string.schedule_room), 18 | onClose = { finish() }) { 19 | } 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/activity/setting/SettingsUiAction.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.setting 2 | 3 | sealed class SettingsUiAction { 4 | object Back : SettingsUiAction() 5 | object Logout : SettingsUiAction() 6 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/compose/CompositionLocal.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.compose 2 | 3 | import androidx.compose.runtime.staticCompositionLocalOf 4 | import io.agora.flat.common.rtc.AgoraRtc 5 | import io.agora.flat.data.AppKVCenter 6 | 7 | val LocalAgoraRtc = staticCompositionLocalOf { 8 | null 9 | } 10 | 11 | 12 | val LocalAppKVCenter = staticCompositionLocalOf { 13 | null 14 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/compose/FlatIcon.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.compose 2 | 3 | import androidx.annotation.DrawableRes 4 | import androidx.compose.material.Icon 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.graphics.Color 8 | import androidx.compose.ui.res.painterResource 9 | import io.agora.flat.ui.theme.Gray_3 10 | import io.agora.flat.ui.theme.Gray_6 11 | import io.agora.flat.ui.theme.isDarkTheme 12 | 13 | 14 | @Composable 15 | fun FlatIcon( 16 | @DrawableRes id: Int, 17 | contentDescription: String? = null, 18 | modifier: Modifier = Modifier, 19 | tint: Color = if (isDarkTheme()) Gray_3 else Gray_6, 20 | ) { 21 | Icon( 22 | painterResource(id = id), 23 | contentDescription = contentDescription, 24 | tint = tint 25 | ) 26 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/compose/FlatLoading.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.compose 2 | 3 | import androidx.compose.foundation.layout.Box 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.material.CircularProgressIndicator 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Alignment 8 | import androidx.compose.ui.Modifier 9 | 10 | @Composable 11 | fun FlatPageLoading() { 12 | CircularProgressIndicator() 13 | } 14 | 15 | @Composable 16 | fun FlatFullPageLoading() { 17 | Box( 18 | modifier = Modifier.fillMaxSize(), 19 | contentAlignment = Alignment.Center 20 | ) { 21 | FlatPageLoading() 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/compose/FlatSpacer.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.compose 2 | 3 | import androidx.compose.foundation.layout.Spacer 4 | import androidx.compose.foundation.layout.height 5 | import androidx.compose.foundation.layout.width 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.unit.dp 9 | 10 | @Composable 11 | fun FlatNormalHorizontalSpacer() { 12 | Spacer(Modifier.width(16.dp)) 13 | } 14 | 15 | @Composable 16 | fun FlatLargeHorizontalSpacer() { 17 | Spacer(Modifier.width(32.dp)) 18 | } 19 | 20 | @Composable 21 | fun FlatSmallVerticalSpacer() { 22 | Spacer(Modifier.height(8.dp)) 23 | } 24 | 25 | @Composable 26 | fun FlatNormalVerticalSpacer() { 27 | Spacer(Modifier.height(16.dp)) 28 | } 29 | 30 | @Composable 31 | fun FlatLargeVerticalSpacer() { 32 | Spacer(Modifier.height(24.dp)) 33 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/manager/RoomErrorManager.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.manager 2 | 3 | import dagger.hilt.android.scopes.ActivityRetainedScoped 4 | import io.agora.flat.ui.util.UiMessage 5 | import kotlinx.coroutines.flow.* 6 | import javax.inject.Inject 7 | 8 | @ActivityRetainedScoped 9 | class RoomErrorManager @Inject constructor() { 10 | private var error = MutableStateFlow(null) 11 | 12 | fun observeError(): Flow = error.asStateFlow().filterNotNull().distinctUntilChanged() 13 | 14 | fun notifyError(text: String, exception: Throwable) { 15 | error.value = UiMessage(text, exception) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/manager/RoomLayoutStateManager.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.manager 2 | 3 | import kotlinx.coroutines.flow.MutableStateFlow 4 | import kotlinx.coroutines.flow.asStateFlow 5 | 6 | /** 7 | * 房间布局状态管理 8 | */ 9 | object RoomLayoutStateManager { 10 | private var state = MutableStateFlow(RoomLayoutState()) 11 | 12 | fun observeState() = state.asStateFlow() 13 | 14 | fun setToolboxMarginBottom(bottom: Int) { 15 | state.value = state.value.copy(toolboxMarginBottom = bottom) 16 | } 17 | } 18 | 19 | data class RoomLayoutState( 20 | val toolboxMarginBottom: Int = 0, 21 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/manager/RoomStateManager.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.manager 2 | 3 | object RoomStateManager { 4 | enum class JoinRoomState { 5 | Init, 6 | Connecting, 7 | Joined, 8 | } 9 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(8.dp), 10 | large = RoundedCornerShape(12.dp), 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/view/LoadingDialog.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.view 2 | 3 | import android.app.Dialog 4 | import android.os.Bundle 5 | import android.view.View 6 | import io.agora.flat.R 7 | import io.agora.flat.databinding.DialogLoadingBinding 8 | 9 | class LoadingDialog : ClassDialogFragment(R.layout.dialog_loading) { 10 | private lateinit var binding: DialogLoadingBinding 11 | 12 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 13 | val dialog = super.onCreateDialog(savedInstanceState) 14 | dialog.window?.setBackgroundDrawableResource(android.R.color.transparent) 15 | return dialog 16 | } 17 | 18 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 19 | super.onViewCreated(view, savedInstanceState) 20 | binding = DialogLoadingBinding.bind(view) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/view/room/ClickHandleView.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.view.room 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.View 6 | import androidx.core.view.isVisible 7 | 8 | /** 9 | * TODO Handle Hide Call Out 10 | */ 11 | class ClickHandleView @JvmOverloads constructor( 12 | context: Context, 13 | attrs: AttributeSet? = null, 14 | defStyleAttr: Int = 0, 15 | ) : View(context, attrs, defStyleAttr) { 16 | 17 | fun show(shown: Boolean, listener: Listener) { 18 | this.isVisible = shown 19 | if (shown) { 20 | this.setOnClickListener { 21 | this.isVisible = false 22 | listener.onClearAll() 23 | } 24 | } 25 | } 26 | 27 | fun interface Listener { 28 | fun onClearAll() 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/ui/viewmodel/FeedbackViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import kotlinx.coroutines.delay 8 | import kotlinx.coroutines.launch 9 | import javax.inject.Inject 10 | 11 | @HiltViewModel 12 | class FeedbackViewModel @Inject constructor() : ViewModel() { 13 | val content: MutableLiveData by lazy { 14 | MutableLiveData() 15 | } 16 | 17 | fun uploadFeedback(text: String) { 18 | viewModelScope.launch { 19 | delay(2000) 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/util/CoroutinesExtensions.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.util 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.launch 6 | 7 | /** 8 | * 存在动画的交互请求,有助于视觉体验的提升 9 | * 例如:点击 loading 后跳转 10 | */ 11 | suspend fun CoroutineScope.runAtLeast(time: Long = 1000, block: suspend () -> T): T { 12 | val start = System.currentTimeMillis() 13 | val result = block() 14 | if (System.currentTimeMillis() - start < time) { 15 | delay(time - (System.currentTimeMillis() - start)) 16 | } 17 | return result 18 | } 19 | 20 | fun CoroutineScope.delayLaunch(time: Long = 200, block: () -> Unit) { 21 | this.launch { 22 | delay(time) 23 | block() 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/util/JsonUtils.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.util 2 | 3 | import com.google.gson.Gson 4 | 5 | object JsonUtils { 6 | val gson = Gson() 7 | 8 | fun toJson(src: Any): String { 9 | return gson.toJson(src) 10 | } 11 | 12 | fun fromJson(json: String, classOfT: Class): T { 13 | return gson.fromJson(json, classOfT) 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/util/Ticker.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.util 2 | 3 | import kotlinx.coroutines.delay 4 | import kotlinx.coroutines.flow.flow 5 | 6 | object Ticker { 7 | fun tickerFlow(period: Long, initialDelay: Long = 0) = flow { 8 | delay(initialDelay) 9 | while (true) { 10 | emit(Unit) 11 | delay(period) 12 | } 13 | } 14 | 15 | fun countDownFlow(totalSeconds: Long) = flow { 16 | for (i in totalSeconds - 1 downTo 0) { 17 | emit(i) 18 | delay(1000) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/io/agora/flat/util/UrlUtils.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.util 2 | 3 | import java.net.HttpURLConnection 4 | import java.net.URL 5 | 6 | object UrlUtils { 7 | fun isResourceExisted(url: String): Boolean { 8 | return try { 9 | val huc: HttpURLConnection = URL(url).openConnection() as HttpURLConnection 10 | huc.requestMethod = "HEAD" 11 | huc.responseCode in 200..299 12 | } catch (ignore: Exception) { 13 | false 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/res/color/color_camera_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_class_room_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_arrow_right_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_arrow_right_red.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_class_room_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_class_room_message.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_class_room_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_class_room_video.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_class_room_voice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_class_room_voice.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_cloudstorage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_cloudstorage.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_flat_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_flat_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_github_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_github_login.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_google_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_google_login.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_home.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_item_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_item_checked.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_item_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_item_unchecked.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_loading.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_message_send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_message_send.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_room.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_room_teacher_leave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_room_teacher_leave.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_room_type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_room_type.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_upload_cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_upload_cancel.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_upload_retry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_upload_retry.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_upload_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_upload_success.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_user_profile_feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_user_profile_feedback.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_wechat_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/ic_wechat_login.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_big_class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/img_big_class.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_cloud_storage_no_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/img_cloud_storage_no_file.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_home_no_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/img_home_no_history.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_home_no_room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/img_home_no_room.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_one_to_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/img_one_to_one.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_pad_login_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/img_pad_login_dark.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_pad_login_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/img_pad_login_light.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_room_video_closed_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/img_room_video_closed_head.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_room_video_closed_head_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/img_room_video_closed_head_big.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_small_class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxhdpi/img_small_class.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cloud_file_audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_cloud_file_audio.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cloud_file_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_cloud_file_folder.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cloud_file_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_cloud_file_image.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cloud_file_others.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_cloud_file_others.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cloud_file_pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_cloud_file_pdf.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cloud_file_ppt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_cloud_file_ppt.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cloud_file_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_cloud_file_video.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cloud_file_word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_cloud_file_word.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cloud_upload_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_cloud_upload_failure.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cloud_upload_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_cloud_upload_success.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_register_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_register_avatar.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_upload_file_audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_upload_file_audio.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_upload_file_doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_upload_file_doc.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_upload_file_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_upload_file_image.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_upload_file_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/ic_upload_file_video.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/img_login_slogan_dark_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/img_login_slogan_dark_en.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/img_login_slogan_dark_zh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/img_login_slogan_dark_zh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/img_login_slogan_light_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/img_login_slogan_light_en.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/img_login_slogan_light_zh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/img_login_slogan_light_zh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/img_login_slogan_pad_dark_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/img_login_slogan_pad_dark_en.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/img_login_slogan_pad_dark_zh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/img_login_slogan_pad_dark_zh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/img_login_slogan_pad_light_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/img_login_slogan_pad_light_en.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/img_login_slogan_pad_light_zh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/drawable-xxxhdpi/img_login_slogan_pad_light_zh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/flat_switch_thumb.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_left.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_right.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_camera_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_camera_shutter_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_class_audio_volume.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_class_cloud_arrow_right.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_class_room_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_class_room_icon_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_class_room_icon_bg_selected.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cloud_list_edit.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cloud_list_option.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cloud_storage_convert_failure.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dialog_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_history.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_item_check_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_loading_rotate.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_login_arrow_down.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_login_email.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_main_home_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_main_home_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_message_muted_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_playback_exit.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_playback_pause.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_playback_start.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_record_arrow_down.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_record_stop.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_red_dot.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_room_audio_state_gray_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_room_audio_state_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_room_cloud_storage_add.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_room_detail_time.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_room_hand_up_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_room_play.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_room_video_state_gray_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_room_video_state_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_room_video_to_collapse.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_send_reward.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_close_account.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_term_of_service.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_text_filed_clear.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_title_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_arrow_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_arrow_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_arrow_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_circle_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_circle_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_circle_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_clear_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_clicker_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_clicker_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_clicker_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_eraser_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_cloudservice_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_cloudservice_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_cloudservice_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_collapsed.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_expanded.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_invite_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_message_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_message_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_message_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_setting_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_state_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_userlist_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_viewfollow_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_viewfollow_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_ext_viewfollow_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_file_upload_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_hand_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_laser_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_line_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_line_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_line_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_nextpage_enabled.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_nextpage_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_page_end_disabled.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_page_end_enabled.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_page_end_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_page_start_disabled.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_page_start_enabled.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_page_start_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_pencil_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_pencil_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_pencil_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_prevpage_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_rectangle_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_rectangle_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_rectangle_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_redo_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_reset_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_scene_add.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_scene_item_id_bg_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_scene_item_id_bg_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_scene_item_id_bg_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_scenes_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_selector_normal.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_selector_selected.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_selector_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_text_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbox_undo_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_user_profile_email.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/launcher_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/playback_thumb_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_item_accept_handup_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_item_window_app_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_black50_round_16_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_black_solid_round_16_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_blue_border_4_round_8_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_blue_border_round_4_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_blue_solid_round_16_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_blue_solid_round_4_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_dialog_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_gray_border_left_round_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_gray_border_right_round_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_gray_border_round_16_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_gray_border_round_40_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_gray_border_round_4_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_gray_border_round_8_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_gray_light_solid_round_12_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_gray_solid_round_12_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_red_border_round_4_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_user_window_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_camera.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/component_extension.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_toolbox_appliance.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_flat_tools_additions.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_time_state.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_window_apps.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/recycler_item_footer.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/raw/loading_dark.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/raw/loading_dark.gif -------------------------------------------------------------------------------- /app/src/main/res/raw/loading_light.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/app/src/main/res/raw/loading_light.gif -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw600dp/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-v28/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 周日 4 | 周一 5 | 周二 6 | 周三 7 | 周四 8 | 周五 9 | 周六 10 | 11 | 12 | 13 | 1月 14 | 2月 15 | 3月 16 | 4月 17 | 5月 18 | 6月 19 | 7月 20 | 8月 21 | 9月 22 | 10月 23 | 11月 24 | 12月 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sun 4 | Mon 5 | Tue 6 | Wed 7 | Thu 8 | Fri 9 | Sat 10 | 11 | 12 | 13 | Jan 14 | Feb 15 | Mar 16 | Apr 17 | May 18 | Jun 19 | Jul 20 | Aug 21 | Sep 22 | Oct 23 | Nov 24 | Dec 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/xml/provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /app/src/test/java/io/agora/flat/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/test/java/io/agora/flat/common/rtc/AgoraRtcTest.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.rtc 2 | 3 | import org.junit.Test 4 | 5 | class AgoraRtcTest { 6 | @Test 7 | fun getOverallQuality() { 8 | assert(AgoraRtc.getOverallQuality(0, 0) == NetworkQuality.Unknown) 9 | assert(AgoraRtc.getOverallQuality(1, 0) == NetworkQuality.Excellent) 10 | assert(AgoraRtc.getOverallQuality(2, 0) == NetworkQuality.Good) 11 | assert(AgoraRtc.getOverallQuality(3, 0) == NetworkQuality.Bad) 12 | assert(AgoraRtc.getOverallQuality(0, 1) == NetworkQuality.Excellent) 13 | assert(AgoraRtc.getOverallQuality(0, 2) == NetworkQuality.Good) 14 | assert(AgoraRtc.getOverallQuality(0, 3) == NetworkQuality.Bad) 15 | assert(AgoraRtc.getOverallQuality(1, 2) == NetworkQuality.Good) 16 | assert(AgoraRtc.getOverallQuality(2, 3) == NetworkQuality.Bad) 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/test/java/io/agora/flat/common/rtm/ClassRtmEventTest.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.common.rtm 2 | 3 | import io.agora.flat.data.model.RoomStatus 4 | import org.junit.Assert.assertTrue 5 | import org.junit.Test 6 | 7 | class ClassRtmEventTest { 8 | @Test 9 | fun `check_parse_room_event`() { 10 | val json = 11 | "{\"t\":\"update-room-status\",\"v\":{\"roomUUID\":\"9be7d709-de2e-4a30-8fb5-0e3c345dc917\",\"status\":\"Stopped\"}}" 12 | val event = ClassRtmEvent.parse(json) 13 | assertTrue( 14 | event == RoomStateEvent( 15 | roomUUID = "9be7d709-de2e-4a30-8fb5-0e3c345dc917", 16 | status = RoomStatus.Stopped 17 | ) 18 | ) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /art/flat-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/art/flat-logo.png -------------------------------------------------------------------------------- /art/flat-showcase-zh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/art/flat-showcase-zh.jpg -------------------------------------------------------------------------------- /art/flat-showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/art/flat-showcase.png -------------------------------------------------------------------------------- /base/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdk 33 8 | 9 | defaultConfig { 10 | minSdk 21 11 | targetSdk 33 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_1_8 24 | targetCompatibility JavaVersion.VERSION_1_8 25 | } 26 | kotlinOptions { 27 | jvmTarget = '1.8' 28 | } 29 | } 30 | 31 | dependencies { 32 | api "org.jetbrains.kotlin:kotlin-stdlib:$rootProject.kotlin_version" 33 | api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.3' 34 | } -------------------------------------------------------------------------------- /base/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /base/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /base/src/main/java/io/agora/flat/di/interfaces/Crashlytics.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.di.interfaces 2 | 3 | import android.content.Context 4 | 5 | interface Crashlytics { 6 | fun init(context: Context) {} 7 | 8 | fun setUserId(id: String) {} 9 | 10 | fun log(priority: Int, tag: String?, message: String, t: Throwable?) {} 11 | } -------------------------------------------------------------------------------- /base/src/main/java/io/agora/flat/di/interfaces/LogConfig.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.di.interfaces 2 | 3 | // here only for aliyun log, in the short term 4 | data class LogConfig( 5 | val ak: String, 6 | val sk: String, 7 | val project: String, 8 | val logstore: String, 9 | val endpoint: String, 10 | ) -------------------------------------------------------------------------------- /base/src/main/java/io/agora/flat/di/interfaces/LogReporter.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.di.interfaces 2 | 3 | import android.content.Context 4 | 5 | interface LogReporter { 6 | fun init(context: Context) {} 7 | 8 | fun setUserId(id: String) {} 9 | 10 | fun report(item: Map) 11 | } -------------------------------------------------------------------------------- /base/src/main/java/io/agora/flat/di/interfaces/PostLoginInitializer.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.di.interfaces 2 | 3 | import android.content.Context 4 | 5 | interface PostLoginInitializer { 6 | fun init(context: Context) 7 | } -------------------------------------------------------------------------------- /base/src/main/java/io/agora/flat/di/interfaces/StartupInitializer.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.di.interfaces 2 | 3 | import android.content.Context 4 | 5 | interface StartupInitializer { 6 | fun init(context: Context) 7 | } -------------------------------------------------------------------------------- /flatrun.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/flatrun.keystore -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netless-io/flat-android/420a1787b121ee7ba42289914abbfb468eb8991c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Aug 20 10:06:41 CST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /logger/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | # Bugly 24 | -dontwarn com.tencent.bugly.** 25 | -keep public class com.tencent.bugly.**{*;} 26 | -------------------------------------------------------------------------------- /logger/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /logger/src/main/java/io/agora/flat/logger/TimberInitializer.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.logger 2 | 3 | import android.content.Context 4 | import io.agora.flat.di.interfaces.Logger 5 | import io.agora.flat.di.interfaces.StartupInitializer 6 | import javax.inject.Inject 7 | 8 | class TimberInitializer @Inject constructor( 9 | private val logger: Logger, 10 | ) : StartupInitializer { 11 | 12 | override fun init(context: Context) { 13 | logger.setup(BuildConfig.DEBUG) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /script/activity/TemplateUiAction.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.{PACKAGE_NAME} 2 | 3 | sealed class {ACTIVITY_NAME}UiAction { 4 | object Close : {ACTIVITY_NAME}UiAction() 5 | data class Bind(val phone: String, val code: String) : {ACTIVITY_NAME}UiAction() 6 | } -------------------------------------------------------------------------------- /script/activity/TemplateUiViewState.kt: -------------------------------------------------------------------------------- 1 | package io.agora.flat.ui.activity.{PACKAGE_NAME} 2 | 3 | data class {ACTIVITY_NAME}UiViewState( 4 | val bindSuccess: Boolean = false, 5 | val binding: Boolean = false, 6 | ) { 7 | companion object { 8 | val Empty = {ACTIVITY_NAME}UiViewState() 9 | } 10 | } -------------------------------------------------------------------------------- /script/create_new_activity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Only Build For Mac OS 3 | 4 | if [ $# != 2 ] ; then 5 | echo "USAGE: $0 " 6 | echo "As: $0 login PhoneLogin" 7 | exit 1; 8 | fi 9 | 10 | PACKAGE_NAME=$1 11 | ACTIVITY_NAME=$2 12 | 13 | cp -rf ./script/activity "app/src/main/java/io/agora/flat/ui/activity/$1" 14 | 15 | cd "app/src/main/java/io/agora/flat/ui/activity/$1" 16 | 17 | # Replace Content Hocker 18 | sed -i "" "s/{PACKAGE_NAME}/$PACKAGE_NAME/g" `grep {PACKAGE_NAME} -rl` 19 | sed -i "" "s/{ACTIVITY_NAME}/$ACTIVITY_NAME/g" `grep {ACTIVITY_NAME} -rl` 20 | 21 | # Replace Template Names 22 | for i in $(ls -chr) 23 | do 24 | NEWNAME=$(echo $i | sed "s/Template/$ACTIVITY_NAME/g") 25 | mv $i $NEWNAME 26 | done 27 | 28 | exit 0 29 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':base' 3 | include ':logger' 4 | rootProject.name = "FlatAndroid" 5 | 6 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 7 | def properties = new Properties() 8 | 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 11 | 12 | def fastboardPath = properties.getProperty("agora.fastboard") 13 | if (fastboardPath != null) { 14 | include ':fastboard' 15 | project(':fastboard').projectDir = new File("$fastboardPath/library") 16 | } 17 | 18 | def syncplayerPath = properties.getProperty("agora.syncplayer") 19 | if (syncplayerPath != null) { 20 | include ':syncplayer' 21 | project(':syncplayer').projectDir = new File("$syncplayerPath/library") 22 | } 23 | } 24 | --------------------------------------------------------------------------------