├── .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 |
4 |
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 |
--------------------------------------------------------------------------------