├── app ├── .gitignore └── src │ ├── main │ ├── res │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.webp │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.webp │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.webp │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.webp │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.webp │ │ ├── layout │ │ │ ├── fragment_splash.xml │ │ │ ├── view_emiji_list.xml │ │ │ ├── dialog_circles_explanation.xml │ │ │ ├── list_item_report_category.xml │ │ │ ├── activity_main.xml │ │ │ └── view_people_search_bar.xml │ │ ├── color │ │ │ ├── send_ic_state_color.xml │ │ │ ├── button_src_state_color.xml │ │ │ └── emoji_chip_background.xml │ │ ├── drawable │ │ │ ├── bg_border.xml │ │ │ ├── bg_border_selected.xml │ │ │ ├── bg_mention_highlight.xml │ │ │ ├── ic_unselected.xml │ │ │ ├── bg_rich_text_menu_button.xml │ │ │ ├── ic_send.xml │ │ │ ├── ic_italic.xml │ │ │ ├── ic_error.xml │ │ │ ├── ic_reply.xml │ │ │ ├── ic_strikethrough.xml │ │ │ ├── ic_text.xml │ │ │ ├── ic_close.xml │ │ │ ├── ic_more.xml │ │ │ ├── ic_fullscreen.xml │ │ │ ├── ic_poll.xml │ │ │ ├── ic_phone.xml │ │ │ ├── ic_image.xml │ │ │ ├── ic_info.xml │ │ │ ├── ic_number_list.xml │ │ │ ├── ic_report.xml │ │ │ ├── ic_winner.xml │ │ │ ├── ic_bold.xml │ │ │ ├── ic_search.xml │ │ │ ├── ic_add_photo.xml │ │ │ ├── ic_bullet_list.xml │ │ │ ├── ic_help.xml │ │ │ ├── ic_ignore.xml │ │ │ ├── ic_round_contacts.xml │ │ │ └── ic_emoji.xml │ │ ├── values │ │ │ ├── styles.xml │ │ │ ├── colors.xml │ │ │ └── attrs.xml │ │ ├── mipmap-anydpi-v26 │ │ │ └── ic_launcher.xml │ │ ├── menu │ │ │ ├── circles_tab_menu.xml │ │ │ └── people_tab_menu.xml │ │ ├── xml │ │ │ └── provider_paths.xml │ │ └── navigation │ │ │ └── nav_graph_bottom_menu.xml │ └── java │ │ └── org │ │ └── futo │ │ └── circles │ │ ├── model │ │ ├── PeopleCategoryTypeArg.kt │ │ ├── MainStyleBarOption.kt │ │ ├── DMListItemPayload.kt │ │ ├── PeopleUserListItemPayload.kt │ │ ├── StyleBarListItem.kt │ │ ├── CircleListItemPayload.kt │ │ ├── ReportCategoryListItem.kt │ │ ├── SetupCircleListItem.kt │ │ ├── GroupListItemPayload.kt │ │ ├── PostItemPayload.kt │ │ ├── CreatePostContent.kt │ │ ├── DMListItem.kt │ │ ├── GroupListItem.kt │ │ └── CircleListItem.kt │ │ ├── feature │ │ ├── timeline │ │ │ ├── post │ │ │ │ ├── create │ │ │ │ │ ├── PostSentListener.kt │ │ │ │ │ └── PreviewPostListener.kt │ │ │ │ └── menu │ │ │ │ │ └── PostMenuListener.kt │ │ │ ├── list │ │ │ │ ├── OnLinkClickedListener.kt │ │ │ │ ├── holder │ │ │ │ │ ├── VideoPlaybackViewHolder.kt │ │ │ │ │ └── TimelineLoadingViewHolder.kt │ │ │ │ ├── OnVideoPlayBackStateListener.kt │ │ │ │ └── PostOptionsListener.kt │ │ │ └── data_source │ │ │ │ └── ReadMessageDataSource.kt │ │ ├── direct │ │ │ ├── timeline │ │ │ │ └── listeners │ │ │ │ │ ├── SendDmMessageListener.kt │ │ │ │ │ └── DmOptionsListener.kt │ │ │ └── tab │ │ │ │ └── DMViewModel.kt │ │ ├── circles │ │ │ └── CirclesViewModel.kt │ │ ├── groups │ │ │ └── GroupsViewModel.kt │ │ ├── share │ │ │ └── group │ │ │ │ └── ShareWithGroupActivity.kt │ │ └── splash │ │ │ └── SplashFragment.kt │ │ └── mapping │ │ └── MatrixUserMapping.kt │ ├── test │ └── java │ │ └── org │ │ └── futo │ │ └── circles │ │ └── ExampleUnitTest.kt │ ├── gplay │ └── AndroidManifest.xml │ └── fdroid │ └── AndroidManifest.xml ├── auth ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── ic_eu.png │ │ │ │ ├── ic_us.png │ │ │ │ ├── explanation_groups.jpg │ │ │ │ ├── explanation_circles_1.jpeg │ │ │ │ ├── explanation_circles_2.jpeg │ │ │ │ ├── explanation_circles_3.jpeg │ │ │ │ ├── explanation_circles_4.jpeg │ │ │ │ ├── ic_file.xml │ │ │ │ ├── ic_info.xml │ │ │ │ └── ic_lightbulb.xml │ │ │ ├── values │ │ │ │ ├── ids.xml │ │ │ │ └── styles.xml │ │ │ └── layout │ │ │ │ └── fragment_empty.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── org │ │ │ └── futo │ │ │ └── circles │ │ │ └── auth │ │ │ ├── bsspeke │ │ │ ├── BSSpekeError.kt │ │ │ └── BSSpekeClientProvider.kt │ │ │ ├── model │ │ │ ├── UIAFlowType.kt │ │ │ ├── AuthUIAScreenNavigationEvent.kt │ │ │ ├── UIANavigationEvent.kt │ │ │ ├── DomainSignupFlows.kt │ │ │ ├── WorkspaceTask.kt │ │ │ ├── TermsListItem.kt │ │ │ ├── SubscriptionReceiptData.kt │ │ │ ├── SetupCirclesListItem.kt │ │ │ ├── SubscriptionListItem.kt │ │ │ ├── ValidateUserIdStatus.kt │ │ │ ├── SwitchUserListItem.kt │ │ │ ├── SecretKeyData.kt │ │ │ ├── ConfirmationType.kt │ │ │ └── CustomUIAuth.kt │ │ │ ├── credentials │ │ │ ├── CredentialsProvider.kt │ │ │ └── CredentialsManager.kt │ │ │ ├── feature │ │ │ ├── explanation │ │ │ │ └── ExplanationDismissListener.kt │ │ │ ├── uia │ │ │ │ ├── flow │ │ │ │ │ └── reauth │ │ │ │ │ │ └── ReAuthCancellationListener.kt │ │ │ │ ├── stages │ │ │ │ │ └── EmptyFragment.kt │ │ │ │ └── UIADataSourceProvider.kt │ │ │ ├── sign_up │ │ │ │ └── SignupSelectDomainListener.kt │ │ │ ├── log_in │ │ │ │ └── suggestion │ │ │ │ │ └── LoginSuggestionListener.kt │ │ │ └── pass_phrase │ │ │ │ ├── recovery │ │ │ │ └── EnterPassPhraseDialogListener.kt │ │ │ │ └── EncryptionAlgorithmHelper.kt │ │ │ └── subscriptions │ │ │ ├── SubscriptionProvider.kt │ │ │ ├── ItemPurchasedListener.kt │ │ │ ├── SubscriptionManager.kt │ │ │ └── IsoPeriod.kt │ ├── test │ │ └── java │ │ │ └── org │ │ │ └── futo │ │ │ └── circles │ │ │ └── auth │ │ │ └── ExampleUnitTest.kt │ ├── fdroid │ │ └── java │ │ │ └── org │ │ │ └── futo │ │ │ └── circles │ │ │ └── auth │ │ │ └── di │ │ │ └── CredentialsModule.kt │ ├── gplay │ │ └── java │ │ │ └── org │ │ │ └── futo │ │ │ └── circles │ │ │ └── auth │ │ │ └── di │ │ │ └── CredentialsModule.kt │ └── androidTest │ │ └── java │ │ └── org │ │ └── futo │ │ └── circles │ │ └── auth │ │ └── ExampleInstrumentedTest.kt └── proguard-rules.pro ├── core ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── futo │ │ │ │ └── circles │ │ │ │ └── core │ │ │ │ ├── model │ │ │ │ ├── MediaType.kt │ │ │ │ ├── PostListItem.kt │ │ │ │ ├── RoomInfo.kt │ │ │ │ ├── TaskStatus.kt │ │ │ │ ├── PollVoteSummaryData.kt │ │ │ │ ├── PushData.kt │ │ │ │ ├── MediaAttachmentInfo.kt │ │ │ │ ├── PickGalleryMediaResultItem.kt │ │ │ │ ├── InviteLoadingEvent.kt │ │ │ │ ├── RoomEventGroupInfo.kt │ │ │ │ ├── CirclesUserSummary.kt │ │ │ │ ├── CreatePollContent.kt │ │ │ │ ├── PollResponseData.kt │ │ │ │ ├── PollOption.kt │ │ │ │ ├── SelectableRoomListItem.kt │ │ │ │ ├── PollState.kt │ │ │ │ ├── PollContent.kt │ │ │ │ ├── DmRoomState.kt │ │ │ │ ├── ReactionsData.kt │ │ │ │ ├── SharableContent.kt │ │ │ │ ├── NotifiableEvent.kt │ │ │ │ ├── GroupedNotificationEvents.kt │ │ │ │ ├── SelectRoomTypeArg.kt │ │ │ │ ├── OtherEventContent.kt │ │ │ │ ├── DiscoveryResponse.kt │ │ │ │ ├── AccessLevelListItem.kt │ │ │ │ ├── RoomRequestTypeArg.kt │ │ │ │ ├── NotificationTestListItem.kt │ │ │ │ ├── PostContentType.kt │ │ │ │ ├── ShareUrlTypeArg.kt │ │ │ │ ├── PostContent.kt │ │ │ │ ├── MediaFileData.kt │ │ │ │ ├── GalleryContentListItem.kt │ │ │ │ ├── AccessLevel.kt │ │ │ │ ├── InviteNotifiableEvent.kt │ │ │ │ ├── PostInfo.kt │ │ │ │ ├── CircleRoomTypeArg.kt │ │ │ │ ├── Post.kt │ │ │ │ ├── NotificationAction.kt │ │ │ │ ├── ResLoadingData.kt │ │ │ │ └── FilterTimelinesListItem.kt │ │ │ │ ├── base │ │ │ │ ├── list │ │ │ │ │ └── IdEntity.kt │ │ │ │ ├── DeepLinkIntentHandler.kt │ │ │ │ ├── ExpandableItemsDataSource.kt │ │ │ │ ├── Constants.kt │ │ │ │ └── SingleEventLiveData.kt │ │ │ │ ├── feature │ │ │ │ ├── blurhash │ │ │ │ │ ├── RGBA.kt │ │ │ │ │ └── ThumbHashImage.kt │ │ │ │ ├── notifications │ │ │ │ │ ├── test │ │ │ │ │ │ └── task │ │ │ │ │ │ │ ├── TestPushClicker.kt │ │ │ │ │ │ │ ├── NotificationQuickFix.kt │ │ │ │ │ │ │ ├── TestPushDisplayEvenReceiver.kt │ │ │ │ │ │ │ ├── NotificationTestsProvider.kt │ │ │ │ │ │ │ └── TestNotificationReceiver.kt │ │ │ │ │ ├── GuardServiceStarter.kt │ │ │ │ │ ├── KeepInternalDistributor.kt │ │ │ │ │ ├── FcmHelper.kt │ │ │ │ │ ├── ProcessedEvent.kt │ │ │ │ │ ├── NotificationActionIds.kt │ │ │ │ │ └── CircularCache.kt │ │ │ │ ├── select_users │ │ │ │ │ └── SelectUsersListener.kt │ │ │ │ ├── room │ │ │ │ │ ├── manage_members │ │ │ │ │ │ ├── change_role │ │ │ │ │ │ │ └── ChangeAccessLevelListener.kt │ │ │ │ │ │ └── ManageMembersOptionsListener.kt │ │ │ │ │ ├── select │ │ │ │ │ │ ├── interfaces │ │ │ │ │ │ │ ├── RoomsListener.kt │ │ │ │ │ │ │ ├── SelectRoomsListener.kt │ │ │ │ │ │ │ └── RoomsPicker.kt │ │ │ │ │ │ ├── SelectRoomsViewModel.kt │ │ │ │ │ │ └── list │ │ │ │ │ │ │ └── SelectRoomsAdapter.kt │ │ │ │ │ └── create │ │ │ │ │ │ └── CreateRoomProgressListener.kt │ │ │ │ ├── share │ │ │ │ │ └── ShareUrl.kt │ │ │ │ ├── markdown │ │ │ │ │ ├── mentions │ │ │ │ │ │ └── plugin │ │ │ │ │ │ │ └── MentionNode.kt │ │ │ │ │ └── span │ │ │ │ │ │ └── MentionSpan.kt │ │ │ │ ├── picker │ │ │ │ │ ├── gallery │ │ │ │ │ │ ├── media │ │ │ │ │ │ │ └── list │ │ │ │ │ │ │ │ └── holder │ │ │ │ │ │ │ │ ├── GalleryTimelineItemViewHolder.kt │ │ │ │ │ │ │ │ └── GalleryTimelineLoadingViewHolder.kt │ │ │ │ │ │ └── rooms │ │ │ │ │ │ │ └── PickGalleryViewModel.kt │ │ │ │ │ └── GetContentWithMultiFilter.kt │ │ │ │ ├── textDrawable │ │ │ │ │ └── ColorGenerator.kt │ │ │ │ ├── user │ │ │ │ │ └── UserOptionsDataSource.kt │ │ │ │ └── circles │ │ │ │ │ └── following │ │ │ │ │ └── list │ │ │ │ │ └── FollowingAdapter.kt │ │ │ │ ├── update │ │ │ │ ├── AppUpdateProvider.kt │ │ │ │ └── CirclesAppUpdateManager.kt │ │ │ │ ├── extensions │ │ │ │ ├── StringExtensions.kt │ │ │ │ ├── HiltExtensions.kt │ │ │ │ ├── TextInputLayoutExtensions.kt │ │ │ │ ├── FileExtensions.kt │ │ │ │ ├── NavControllerExtensions.kt │ │ │ │ ├── RoomSummaryExtensions.kt │ │ │ │ ├── ViewModelExtensions.kt │ │ │ │ ├── SearchViewExtensions.kt │ │ │ │ ├── MatrixUserExtensions.kt │ │ │ │ ├── TimelineEventExtensions.kt │ │ │ │ └── MediaContentDataExtensions.kt │ │ │ │ ├── provider │ │ │ │ ├── MatrixNotificationSetupListener.kt │ │ │ │ └── MatrixInstanceProvider.kt │ │ │ │ ├── mapping │ │ │ │ └── MatrixUserMapping.kt │ │ │ │ ├── utils │ │ │ │ ├── HomeServerUtils.kt │ │ │ │ └── UserIdUtils.kt │ │ │ │ ├── glide │ │ │ │ └── LocalFileHelper.kt │ │ │ │ └── view │ │ │ │ └── NetworkRequiredButton.kt │ │ ├── res │ │ │ ├── animator │ │ │ │ ├── nav_default_enter_anim.xml │ │ │ │ ├── nav_default_exit_anim.xml │ │ │ │ ├── nav_default_pop_enter_anim.xml │ │ │ │ └── nav_default_pop_exit_anim.xml │ │ │ ├── values │ │ │ │ ├── ids.xml │ │ │ │ ├── dimen.xml │ │ │ │ └── attrs.xml │ │ │ ├── drawable │ │ │ │ ├── bg_border.xml │ │ │ │ ├── ic_unselected.xml │ │ │ │ ├── ic_keyboard_arrow_down.xml │ │ │ │ ├── ic_add.xml │ │ │ │ ├── ic_play_round.xml │ │ │ │ ├── ic_error.xml │ │ │ │ ├── ic_resend.xml │ │ │ │ ├── ic_video.xml │ │ │ │ ├── ic_download.xml │ │ │ │ ├── ic_check.xml │ │ │ │ ├── ic_keyboard_arrow_up.xml │ │ │ │ ├── ic_app_version.xml │ │ │ │ ├── ic_create.xml │ │ │ │ ├── ic_close.xml │ │ │ │ ├── ic_check_circle.xml │ │ │ │ ├── ic_direct_messages.xml │ │ │ │ ├── ic_gallery.xml │ │ │ │ ├── ic_phone.xml │ │ │ │ ├── ic_logout.xml │ │ │ │ ├── ic_info.xml │ │ │ │ ├── ic_invite.xml │ │ │ │ ├── ic_filter.xml │ │ │ │ ├── ic_notifications.xml │ │ │ │ ├── ic_lock.xml │ │ │ │ ├── ic_seen.xml │ │ │ │ ├── ic_delete.xml │ │ │ │ ├── ic_lock_open.xml │ │ │ │ ├── ic_knock_requests.xml │ │ │ │ ├── ic_switch_user.xml │ │ │ │ ├── ic_deactivate_account.xml │ │ │ │ ├── ic_edit.xml │ │ │ │ ├── ic_reload.xml │ │ │ │ ├── ic_add_photo.xml │ │ │ │ ├── ic_curved_arrow.xml │ │ │ │ ├── ic_unfollow.xml │ │ │ │ ├── ic_photo.xml │ │ │ │ ├── ic_mention.xml │ │ │ │ ├── ic_notifications_off.xml │ │ │ │ ├── ic_ban.xml │ │ │ │ └── ic_person_remove.xml │ │ │ ├── anim │ │ │ │ ├── alpha_show.xml │ │ │ │ ├── slide_in.xml │ │ │ │ └── slide_out.xml │ │ │ ├── layout │ │ │ │ ├── list_item_invite_header.xml │ │ │ │ ├── fragment_pick_gallery.xml │ │ │ │ ├── list_item_invite_member.xml │ │ │ │ ├── list_item_no_results.xml │ │ │ │ ├── view_spinner_item.xml │ │ │ │ ├── list_item_timeline_loading.xml │ │ │ │ ├── list_item_chip.xml │ │ │ │ ├── dialog_loading.xml │ │ │ │ ├── list_item_access_level.xml │ │ │ │ └── list_item_invite_notification.xml │ │ │ ├── xml │ │ │ │ └── bg_chip.xml │ │ │ ├── menu │ │ │ │ ├── user_menu.xml │ │ │ │ └── timeline_menu.xml │ │ │ ├── values-night │ │ │ │ └── color.xml │ │ │ └── navigation │ │ │ │ ├── filter_timelines_nav_graph.xml │ │ │ │ └── create_room_nav_graph.xml │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── org │ │ │ └── futo │ │ │ └── circles │ │ │ └── core │ │ │ └── ExampleUnitTest.kt │ ├── fdroid │ │ └── java │ │ │ └── org │ │ │ └── futo │ │ │ └── circles │ │ │ └── core │ │ │ └── di │ │ │ └── AppUpdateModule.kt │ ├── gplay │ │ └── java │ │ │ └── org │ │ │ └── futo │ │ │ └── circles │ │ │ └── core │ │ │ └── di │ │ │ └── AppUpdateModule.kt │ └── androidTest │ │ └── java │ │ └── org │ │ └── futo │ │ └── circles │ │ └── core │ │ └── ExampleInstrumentedTest.kt └── proguard-rules.pro ├── gallery ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ └── styles.xml │ │ │ ├── menu │ │ │ │ ├── photos_tab_menu.xml │ │ │ │ └── gallery_image_menu.xml │ │ │ ├── transition │ │ │ │ ├── grid_exit_transition.xml │ │ │ │ └── image_shared_element_transition.xml │ │ │ ├── drawable │ │ │ │ ├── ic_check_circle.xml │ │ │ │ ├── ic_create.xml │ │ │ │ └── ic_backup.xml │ │ │ └── layout │ │ │ │ └── fragment_select_galleries.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── org │ │ │ └── futo │ │ │ └── circles │ │ │ └── gallery │ │ │ ├── model │ │ │ ├── MediaFolderListItem.kt │ │ │ └── MediaToBackupItem.kt │ │ │ └── feature │ │ │ ├── PhotosViewModel.kt │ │ │ ├── select │ │ │ └── SelectGalleriesViewModel.kt │ │ │ └── share │ │ │ └── UploadToGalleryActivity.kt │ └── test │ │ └── java │ │ └── org │ │ └── futo │ │ └── circles │ │ └── gallery │ │ └── ExampleUnitTest.kt └── proguard-rules.pro ├── settings ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── org │ │ │ │ └── futo │ │ │ │ └── circles │ │ │ │ └── settings │ │ │ │ ├── SessionHolderActivity.kt │ │ │ │ ├── model │ │ │ │ ├── QrState.kt │ │ │ │ └── ActiveSubscriptionInfo.kt │ │ │ │ └── view │ │ │ │ └── EditEmailView.kt │ │ └── res │ │ │ ├── drawable │ │ │ ├── ic_email.xml │ │ │ ├── ic_payment.xml │ │ │ ├── ic_key.xml │ │ │ ├── ic_verified.xml │ │ │ ├── ic_round_photo_library.xml │ │ │ └── ic_unverified.xml │ │ │ └── layout │ │ │ └── activity_qr_scanner.xml │ ├── test │ │ └── java │ │ │ └── org │ │ │ └── futo │ │ │ └── circles │ │ │ └── settings │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── org │ │ └── futo │ │ └── circles │ │ └── settings │ │ └── ExampleInstrumentedTest.kt └── proguard-rules.pro ├── fastlane ├── metadata │ └── android │ │ └── en-US │ │ ├── title.txt │ │ ├── short_description.txt │ │ ├── changelogs │ │ ├── 4401.txt │ │ ├── 33.txt │ │ ├── 27.txt │ │ ├── 29.txt │ │ ├── 24.txt │ │ ├── 34.txt │ │ ├── 28.txt │ │ ├── 30.txt │ │ ├── 4301.txt │ │ ├── 3701.txt │ │ ├── 31.txt │ │ ├── 25.txt │ │ ├── 4201.txt │ │ ├── 4001.txt │ │ ├── 3901.txt │ │ ├── 3501.txt │ │ ├── 3601.txt │ │ ├── 3801.txt │ │ ├── 4101.txt │ │ └── 32.txt │ │ ├── images │ │ ├── icon.png │ │ ├── featureGraphic.jpeg │ │ └── phoneScreenshots │ │ │ ├── 1_en-US.png │ │ │ ├── 2_en-US.png │ │ │ ├── 3_en-US.png │ │ │ ├── 4_en-US.png │ │ │ ├── 5_en-US.png │ │ │ ├── 6_en-US.png │ │ │ └── 7_en-US.png │ │ └── full_description.txt └── Appfile ├── Gemfile ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── resources └── img │ ├── f-droid-badge.png │ └── google-play-badge.png ├── .github └── ISSUE_TEMPLATE │ └── config.yml ├── modules_release_clean.sh └── settings.gradle.kts /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /auth/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /auth/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gallery/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /gallery/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /settings/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /settings/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Circles -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | Secure social sharing for friends and families -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/4401.txt: -------------------------------------------------------------------------------- 1 | - Direct messages 2 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/33.txt: -------------------------------------------------------------------------------- 1 | - New default domains 2 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /resources/img/f-droid-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/resources/img/f-droid-badge.png -------------------------------------------------------------------------------- /resources/img/google-play-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/resources/img/google-play-badge.png -------------------------------------------------------------------------------- /auth/src/main/res/drawable/ic_eu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/auth/src/main/res/drawable/ic_eu.png -------------------------------------------------------------------------------- /auth/src/main/res/drawable/ic_us.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/auth/src/main/res/drawable/ic_us.png -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/MediaType.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | enum class MediaType { Image, Video } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/27.txt: -------------------------------------------------------------------------------- 1 | v1.0.17 2 | - App No Internet behavior 3 | - Knock requests managing 4 | - Android 14 support -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/29.txt: -------------------------------------------------------------------------------- 1 | - Rust crypto SDK 2 | - New Circles logo 3 | - Blur images in invitations from unknown users -------------------------------------------------------------------------------- /auth/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/24.txt: -------------------------------------------------------------------------------- 1 | v1.0.15 2 | - new encryption algorithm 3 | - timeline thumbnails 4 | - thumbhash for timeline placeholders -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/34.txt: -------------------------------------------------------------------------------- 1 | - New default domains 2 | - circles.futo.org domain for deep links 3 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /auth/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /auth/src/main/res/drawable/explanation_groups.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/auth/src/main/res/drawable/explanation_groups.jpg -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/bsspeke/BSSpekeError.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.bsspeke 2 | 3 | class BSSpekeError(override val message: String) : Exception() -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/UIAFlowType.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | enum class UIAFlowType { Login, Signup, ReAuth, ForgotPassword } -------------------------------------------------------------------------------- /auth/src/main/res/drawable/explanation_circles_1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/auth/src/main/res/drawable/explanation_circles_1.jpeg -------------------------------------------------------------------------------- /auth/src/main/res/drawable/explanation_circles_2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/auth/src/main/res/drawable/explanation_circles_2.jpeg -------------------------------------------------------------------------------- /auth/src/main/res/drawable/explanation_circles_3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/auth/src/main/res/drawable/explanation_circles_3.jpeg -------------------------------------------------------------------------------- /auth/src/main/res/drawable/explanation_circles_4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/auth/src/main/res/drawable/explanation_circles_4.jpeg -------------------------------------------------------------------------------- /core/src/main/res/animator/nav_default_enter_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /core/src/main/res/animator/nav_default_exit_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /gallery/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 57dp 4 | -------------------------------------------------------------------------------- /settings/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/PeopleCategoryTypeArg.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | enum class PeopleCategoryTypeArg { Followers, Following, Other, Ignored } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/base/list/IdEntity.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.base.list 2 | 3 | interface IdEntity { 4 | val id: IdClass 5 | } -------------------------------------------------------------------------------- /core/src/main/res/animator/nav_default_pop_enter_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /core/src/main/res/animator/nav_default_pop_exit_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/28.txt: -------------------------------------------------------------------------------- 1 | v1.0.18 2 | - "Help" menu item for Groups and Circles 3 | - New share room url formatting 4 | - Knock requests "reason" message -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/base/DeepLinkIntentHandler.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.base 2 | 3 | interface DeepLinkIntentHandler { 4 | fun onNewIntent() 5 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PostListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/30.txt: -------------------------------------------------------------------------------- 1 | - Rust crypto SDK 2 | - New Circles logo 3 | - Blur images in invitations from unknown users 4 | - Fix Kotlin to Rust migration -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/featureGraphic.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/fastlane/metadata/android/en-US/images/featureGraphic.jpeg -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/RoomInfo.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | data class RoomInfo( 4 | val title: String, 5 | val avatarUrl: String 6 | ) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/blurhash/RGBA.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.blurhash 2 | 3 | data class RGBA(var r: Float, var g: Float, var b: Float, var a: Float) -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/4301.txt: -------------------------------------------------------------------------------- 1 | - Simplified Sign up flow 2 | - Fixed crash on Creating new post 3 | - Onboarding design updates 4 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/TaskStatus.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | enum class TaskStatus { 4 | IDLE, 5 | RUNNING, 6 | SUCCESS, 7 | FAILED 8 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/6_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/6_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/7_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/circles-project/circles-android/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/7_en-US.png -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/timeline/post/create/PostSentListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.timeline.post.create 2 | 3 | interface PostSentListener { 4 | fun onPostSent() 5 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PollVoteSummaryData.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | data class PollVoteSummaryData( 4 | val total: Int, 5 | val percentage: Double 6 | ) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/blurhash/ThumbHashImage.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.blurhash 2 | 3 | data class ThumbHashImage(var width: Int, var height: Int, var rgba: ByteArray) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/update/AppUpdateProvider.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.update 2 | 3 | interface AppUpdateProvider { 4 | 5 | fun getManager(): CirclesAppUpdateManager? 6 | 7 | } -------------------------------------------------------------------------------- /core/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/MainStyleBarOption.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | enum class MainStyleBarOption { 4 | Media, 5 | Emoji, 6 | Mention, 7 | Link, 8 | TextStyle 9 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/3701.txt: -------------------------------------------------------------------------------- 1 | - Email address management 2 | - New People tab 3 | - Forgot password 4 | - Configurable default user power level for room 5 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/timeline/list/OnLinkClickedListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.timeline.list 2 | 3 | 4 | interface OnLinkClickedListener { 5 | fun onLinkClicked(url: String) 6 | } -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/credentials/CredentialsProvider.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.credentials 2 | 3 | interface CredentialsProvider { 4 | 5 | fun getManager(): CredentialsManager? 6 | 7 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PushData.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | data class PushData( 4 | val eventId: String?, 5 | val roomId: String?, 6 | val unread: Int?, 7 | ) 8 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/31.txt: -------------------------------------------------------------------------------- 1 | - Support for refresh tokens 2 | - Fixed markdown editor 3 | - Ability to change poll vote 4 | - Fixed Bcrypt backup restoration 5 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/feature/explanation/ExplanationDismissListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.feature.explanation 2 | 3 | interface ExplanationDismissListener { 4 | 5 | fun onDismissPopUp() 6 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/test/task/TestPushClicker.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications.test.task 2 | 3 | interface TestPushClicker { 4 | fun onTestPushClicked() 5 | } -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | json_key_file("google_play_api_key.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one 2 | package_name("org.futo.circles") # e.g. com.krausefx.app 3 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/feature/uia/flow/reauth/ReAuthCancellationListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.feature.uia.flow.reauth 2 | 3 | interface ReAuthCancellationListener { 4 | fun onReAuthCanceled() 5 | } -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/AuthUIAScreenNavigationEvent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | enum class AuthUIAScreenNavigationEvent { 4 | Home, 5 | ConfigureWorkspace, 6 | PassPhrase 7 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/GuardServiceStarter.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications 2 | 3 | interface GuardServiceStarter { 4 | fun start() {} 5 | fun stop() {} 6 | } 7 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/select_users/SelectUsersListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.select_users 2 | 3 | interface SelectUsersListener { 4 | fun onUserSelected(usersIds: List) 5 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/MediaAttachmentInfo.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | data class MediaAttachmentInfo( 4 | val name: String, 5 | val size: Long, 6 | val mimeType: String 7 | ) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PickGalleryMediaResultItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | data class PickGalleryMediaResultItem( 4 | val uriString: String, 5 | val mediaTypeOrdinal: Int 6 | ) -------------------------------------------------------------------------------- /settings/src/main/java/org/futo/circles/settings/SessionHolderActivity.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.settings 2 | 3 | interface SessionHolderActivity { 4 | fun clearSessionAndRestart() 5 | 6 | fun stopSyncAndRestart() 7 | } -------------------------------------------------------------------------------- /auth/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/25.txt: -------------------------------------------------------------------------------- 1 | v1.0.16 2 | - People space 3 | - Configure Workspace 4 | - Removed System notices 5 | - Circle/Group explanation dialogs 6 | - Offline browsing of People tab 7 | - Raw recovery key support -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/4201.txt: -------------------------------------------------------------------------------- 1 | - Removed Jdenticon user's icon placeholder 2 | - Added "Invite to follow me" 3 | - Added "Requests for invitation" on Circles/Groups tabs 4 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/test/task/NotificationQuickFix.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications.test.task 2 | 3 | abstract class NotificationQuickFix { 4 | abstract fun runFix() 5 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignupSelectDomainListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.feature.sign_up 2 | 3 | interface SignupSelectDomainListener { 4 | 5 | fun onSignupDomainSelected(domain: String) 6 | 7 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/4001.txt: -------------------------------------------------------------------------------- 1 | - Fixed create group/circle/gallery navigation 2 | - Share poll as image 3 | - Improved offline app browsing experience 4 | - Shared Galleries section 5 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/test/task/TestPushDisplayEvenReceiver.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications.test.task 2 | 3 | interface TestPushDisplayEvenReceiver { 4 | fun onTestPushDisplayed() 5 | } -------------------------------------------------------------------------------- /gallery/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/InviteLoadingEvent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | 4 | data class InviteLoadingEvent( 5 | val userId: String = "", 6 | val roomName: String = "", 7 | val isLoading: Boolean = true 8 | ) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/update/CirclesAppUpdateManager.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.update 2 | 3 | import android.app.Activity 4 | 5 | interface CirclesAppUpdateManager { 6 | fun launchUpdateIfAvailable(activity: Activity) 7 | 8 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/3901.txt: -------------------------------------------------------------------------------- 1 | - Remove BCrypt backup support 2 | - Show all timeline events types with dev mode enabled 3 | - Improve paging for timeline 4 | - App update api (Google Play version) 5 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/StringExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import android.util.Patterns 4 | 5 | fun CharSequence?.isValidEmail() = 6 | !isNullOrEmpty() && Patterns.EMAIL_ADDRESS.matcher(this).matches() -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/feature/uia/stages/EmptyFragment.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.feature.uia.stages 2 | 3 | import androidx.fragment.app.Fragment 4 | import org.futo.circles.auth.R 5 | 6 | class EmptyFragment : Fragment(R.layout.fragment_empty) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/test/task/NotificationTestsProvider.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications.test.task 2 | 3 | interface NotificationTestsProvider { 4 | fun getTestsList(): List 5 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/3501.txt: -------------------------------------------------------------------------------- 1 | - Fixed posting replies 2 | - UnifiedPush as default distributor(F-Droid) 3 | - Fixed registering UnifiedPush for multiple accounts 4 | - Fixed markdown rendering after switching user 5 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/DMListItemPayload.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | data class DMListItemPayload( 4 | val timestamp: Long?, 5 | val unreadCount: Int?, 6 | val userName: String?, 7 | val avatarUrl: String?, 8 | val userId: String 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/PeopleUserListItemPayload.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | import org.futo.circles.core.model.CirclesUserSummary 4 | 5 | data class PeopleUserListItemPayload( 6 | val user: CirclesUserSummary?, 7 | val categoryCount: Int? 8 | ) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/RoomEventGroupInfo.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | 4 | data class RoomEventGroupInfo( 5 | val roomId: String, 6 | val roomDisplayName: String = "" 7 | ) { 8 | var isUpdated: Boolean = false 9 | } 10 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/feature/log_in/suggestion/LoginSuggestionListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.feature.log_in.suggestion 2 | 3 | interface LoginSuggestionListener { 4 | 5 | fun onLoginSuggestionApplied(userId: String, isForgotPassword: Boolean) 6 | 7 | } -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/UIANavigationEvent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | 4 | enum class UIANavigationEvent { 5 | TokenValidation, 6 | Subscription, 7 | AcceptTerm, 8 | ValidateEmail, 9 | Password, 10 | Username 11 | } -------------------------------------------------------------------------------- /gallery/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/color/send_ic_state_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/room/manage_members/change_role/ChangeAccessLevelListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.room.manage_members.change_role 2 | 3 | interface ChangeAccessLevelListener { 4 | fun onChangeAccessLevel(userId: String, levelValue: Int) 5 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Feb 14 14:59:49 EET 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/timeline/list/holder/VideoPlaybackViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.timeline.list.holder 2 | 3 | interface VideoPlaybackViewHolder : ImageMediaViewHolder { 4 | 5 | fun stopVideo(shouldNotify: Boolean = true) 6 | 7 | fun playVideo() 8 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/3601.txt: -------------------------------------------------------------------------------- 1 | - Limit displayed aspect ratios for media posts 2 | - Removed Create room delay 3 | - Fixed Change password flow 4 | - Inline Markdown fixes 5 | - Fixed views count on post 6 | - Fixed screens state saving 7 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/StyleBarListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | 5 | data class StyleBarListItem( 6 | override val id: Int, 7 | val iconResId: Int, 8 | val isSelected: Boolean = false 9 | ) : IdEntity -------------------------------------------------------------------------------- /app/src/main/res/color/button_src_state_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/3801.txt: -------------------------------------------------------------------------------- 1 | - Circle's timelines filter 2 | - Screens animation 3 | - Credentials manager to save/autofill password 4 | - Adaptive app icon 5 | - Video playback in timeline post 6 | - What's new dialog 7 | - Mutual users section 8 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/CircleListItemPayload.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | data class CircleListItemPayload( 4 | val followersCount: Int?, 5 | val followedByCount: Int?, 6 | val unreadCount: Int?, 7 | val knocksCount: Int?, 8 | val needUpdateFullItem: Boolean 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/ReportCategoryListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | 5 | data class ReportCategoryListItem( 6 | override val id: Int, 7 | val name: String, 8 | val isSelected: Boolean = false 9 | ) : IdEntity -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/HiltExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import androidx.lifecycle.SavedStateHandle 4 | 5 | fun SavedStateHandle.getOrThrow(key: String): T = 6 | this[key] ?: throw IllegalArgumentException("SavedStateHandle param $key is missing") -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/CirclesUserSummary.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | 5 | data class CirclesUserSummary( 6 | override val id: String, 7 | val name: String, 8 | val avatarUrl: String 9 | ) : IdEntity -------------------------------------------------------------------------------- /app/src/main/res/color/emoji_chip_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/CreatePollContent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.matrix.android.sdk.api.session.room.model.message.PollType 4 | 5 | data class CreatePollContent( 6 | val pollType: PollType, 7 | val question: String, 8 | val options: List 9 | ) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PollResponseData.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | data class PollResponseData( 4 | val myVote: String?, 5 | val votes: Map?, 6 | val totalVotes: Int, 7 | val winnerVoteCount: Int, 8 | val isClosed: Boolean 9 | ) -------------------------------------------------------------------------------- /gallery/src/main/res/menu/photos_tab_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/DomainSignupFlows.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | import org.matrix.android.sdk.api.auth.registration.Stage 4 | 5 | data class DomainSignupFlows( 6 | val domain: String, 7 | val freeStages: List?, 8 | val subscriptionStages: List? 9 | ) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/room/select/interfaces/RoomsListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.room.select.interfaces 2 | 3 | import org.futo.circles.core.model.SelectableRoomListItem 4 | 5 | interface RoomsListener { 6 | fun onRoomsListChanged(rooms: List) 7 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PollOption.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | data class PollOption( 4 | val optionId: String, 5 | val optionAnswer: String, 6 | val voteCount: Int, 7 | val voteProgress: Int, 8 | val isMyVote: Boolean, 9 | val isWinner: Boolean 10 | ) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/SelectableRoomListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | 5 | data class SelectableRoomListItem( 6 | override val id: String, 7 | val info: RoomInfo, 8 | val isSelected: Boolean 9 | ) : IdEntity -------------------------------------------------------------------------------- /core/src/main/res/drawable/bg_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/WorkspaceTask.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | import android.net.Uri 4 | import org.futo.circles.core.model.CirclesRoom 5 | 6 | data class WorkspaceTask( 7 | val room: CirclesRoom, 8 | val name: String? = null, 9 | val uri: Uri? = null 10 | ) 11 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/room/select/interfaces/SelectRoomsListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.room.select.interfaces 2 | 3 | import org.futo.circles.core.model.SelectableRoomListItem 4 | 5 | interface SelectRoomsListener { 6 | fun onRoomsSelected(rooms: List) 7 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PollState.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | enum class PollState { Sending, Ready, Voted, Ended } 4 | 5 | fun PollState.canEdit() = this == PollState.Sending || this == PollState.Ready 6 | 7 | fun PollState.canVote() = this != PollState.Sending && this != PollState.Ended -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/provider/MatrixNotificationSetupListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.provider 2 | 3 | import org.matrix.android.sdk.api.session.Session 4 | 5 | interface MatrixNotificationSetupListener { 6 | 7 | fun onStartWithSession(session: Session) 8 | 9 | fun onStop() 10 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_border_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_mention_highlight.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /settings/src/main/java/org/futo/circles/settings/model/QrState.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.settings.model 2 | 3 | sealed class QrState 4 | 5 | data object QrLoading : QrState() 6 | 7 | data class QrReady(val qrText: String) : QrState() 8 | 9 | data object QrSuccess : QrState() 10 | 11 | data object QrCanceled : QrState() -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/TermsListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | 5 | data class TermsListItem( 6 | override val id: Int, 7 | val name: String, 8 | val url: String, 9 | val isChecked: Boolean = false 10 | ) : IdEntity -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PollContent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | data class PollContent( 4 | val question: String, 5 | val state: PollState, 6 | val totalVotes: Int, 7 | val options: List, 8 | val isClosedType: Boolean 9 | ) : PostContent(PostContentType.POLL_CONTENT) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/DmRoomState.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | sealed class DmRoomState 4 | 5 | data class DmConnected(val roomId: String) : DmRoomState() 6 | data class DmHasInvite(val roomId: String) : DmRoomState() 7 | data object DmInviteSent : DmRoomState() 8 | data object DmNotFound : DmRoomState() -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/ReactionsData.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | 5 | data class ReactionsData( 6 | val key: String, 7 | val count: Int, 8 | val addedByMe: Boolean 9 | ) : IdEntity { 10 | override val id: String = key 11 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/4101.txt: -------------------------------------------------------------------------------- 1 | - Support of dehydrated devices 2 | - Profile updates via presence system 3 | - Post composer awaits for post to be sent 4 | - Removed Connections 5 | - Removed Gallery (still available in advanced settings) 6 | - Replaced Configure Workspace with new setup flow 7 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/share/ShareUrl.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.share 2 | 3 | import org.futo.circles.core.model.ShareUrlTypeArg 4 | 5 | const val BASE_SHARE_URL = "https://circles.futo.org/" 6 | 7 | fun buildShareRoomUrl(type: ShareUrlTypeArg, roomId: String) = 8 | BASE_SHARE_URL + type.typeKey + "/$roomId" 9 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/SharableContent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import android.net.Uri 4 | 5 | sealed class ShareableContent 6 | 7 | data class TextShareable(val text: String) : ShareableContent() 8 | data class MediaShareable(val uriToFile: Uri, val mimeType: String) : ShareableContent() 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/direct/timeline/listeners/SendDmMessageListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.direct.timeline.listeners 2 | 3 | interface SendDmMessageListener { 4 | 5 | fun onAddEmojiToMessageClicked() 6 | 7 | fun onSendTextMessageClicked(message: String) 8 | 9 | fun onSendMediaButtonClicked() 10 | 11 | } -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/SubscriptionReceiptData.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | data class SubscriptionReceiptData( 4 | val productId: String, 5 | val purchaseToken: String, 6 | val orderId: String, 7 | val packageName: String, 8 | val purchaseTime: Long, 9 | val isAutoRenewing: Boolean 10 | ) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/TextInputLayoutExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import com.google.android.material.textfield.TextInputLayout 4 | 5 | fun TextInputLayout.getText(trim: Boolean = true): String { 6 | val text = editText?.text?.toString() ?: "" 7 | return if (trim) text.trim() else text 8 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/room/select/interfaces/RoomsPicker.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.room.select.interfaces 2 | 3 | import org.futo.circles.core.model.SelectableRoomListItem 4 | 5 | interface RoomsPicker { 6 | var selectRoomsListener: SelectRoomsListener? 7 | fun getSelectedRooms(): List 8 | } -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/SetupCircleListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | import android.net.Uri 4 | import org.futo.circles.core.base.list.IdEntity 5 | 6 | data class SetupCircleListItem( 7 | override val id: Int, 8 | val name: String, 9 | val userName: String, 10 | val coverUri: Uri? = null 11 | ) : IdEntity -------------------------------------------------------------------------------- /core/src/main/res/values/dimen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1dp 4 | 24dp 5 | 100dp 6 | 120dp 7 | 50dp 8 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/32.txt: -------------------------------------------------------------------------------- 1 | - Initial sync improvements 2 | - Jdenticon for user's avatar placeholders 3 | - Media usage info (only for servers that support it) 4 | - Connect with other user option 5 | - New screens for invitations 6 | - Ignored user section in Settings 7 | - More categories on People tab 8 | - Bug fixes and performance improvements -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/timeline/list/OnVideoPlayBackStateListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.timeline.list 2 | 3 | import org.futo.circles.feature.timeline.list.holder.VideoPlaybackViewHolder 4 | 5 | interface OnVideoPlayBackStateListener { 6 | 7 | fun onVideoPlaybackStateChanged(holder: VideoPlaybackViewHolder, isPlaying: Boolean) 8 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/NotifiableEvent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import java.io.Serializable 4 | 5 | sealed interface NotifiableEvent : Serializable { 6 | val eventId: String 7 | val editedEventId: String? 8 | val canBeReplaced: Boolean 9 | val isRedacted: Boolean 10 | val isUpdated: Boolean 11 | } -------------------------------------------------------------------------------- /core/src/main/res/anim/alpha_show.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_unselected.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /gallery/src/main/java/org/futo/circles/gallery/model/MediaFolderListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.gallery.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | 5 | data class MediaFolderListItem( 6 | override val id: String, 7 | val displayName: String, 8 | val size: Long, 9 | val isSelected: Boolean = false 10 | ) : IdEntity -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/GroupListItemPayload.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | data class GroupListItemPayload( 4 | val topic: String?, 5 | val isEncrypted: Boolean?, 6 | val membersCount: Int?, 7 | val timestamp: Long?, 8 | val unreadCount: Int?, 9 | val knocksCount: Int?, 10 | val needUpdateFullItem: Boolean 11 | ) -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/subscriptions/SubscriptionProvider.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.subscriptions 2 | 3 | import androidx.fragment.app.Fragment 4 | 5 | interface SubscriptionProvider { 6 | 7 | fun getManager( 8 | fragment: Fragment, 9 | itemPurchaseListener: ItemPurchasedListener? 10 | ): SubscriptionManager 11 | 12 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Circles web 4 | url: https://circles.futo.org 5 | about: You can find our contact info here. 6 | - name: Circles Matrix room 7 | url: https://matrix.to/#/!gcbIHWAYBBmvITkQIn:matrix.org?via=matrix.org&via=envs.net&via=tchncs.de 8 | about: Circle's news and discussions here. -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/SetupCirclesListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | import android.net.Uri 4 | import org.futo.circles.core.base.list.IdEntity 5 | 6 | data class SetupCirclesListItem( 7 | val name: String, 8 | val uri: Uri? = null 9 | ) : IdEntity { 10 | override val id: String 11 | get() = name 12 | } -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/SubscriptionListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | 5 | data class SubscriptionListItem( 6 | override val id: String, 7 | val name: String, 8 | val description: String, 9 | val price: String, 10 | val duration: String 11 | ) : IdEntity -------------------------------------------------------------------------------- /core/src/main/res/anim/slide_in.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | -------------------------------------------------------------------------------- /core/src/main/res/anim/slide_out.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | -------------------------------------------------------------------------------- /core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/markdown/mentions/plugin/MentionNode.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.markdown.mentions.plugin 2 | 3 | import org.commonmark.node.CustomNode 4 | import org.commonmark.node.Visitor 5 | 6 | 7 | class MentionNode : CustomNode() { 8 | override fun accept(visitor: Visitor) { 9 | visitor.visit(this) 10 | } 11 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/room/create/CreateRoomProgressListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.room.create 2 | 3 | enum class CreateRoomProgressStage { CreateRoom, CreateTimeline, SetParentRelations, SetTimelineRelations, Finished } 4 | 5 | interface CreateRoomProgressListener { 6 | 7 | fun onProgressUpdated(event: CreateRoomProgressStage) 8 | 9 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_unselected.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/subscriptions/ItemPurchasedListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.subscriptions 2 | 3 | import org.futo.circles.auth.model.SubscriptionReceiptData 4 | 5 | 6 | interface ItemPurchasedListener { 7 | 8 | fun onItemPurchased(subscriptionReceiptData: SubscriptionReceiptData) 9 | 10 | fun onPurchaseFailed(errorCode: Int) 11 | 12 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/GroupedNotificationEvents.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.feature.notifications.ProcessedEvent 4 | 5 | data class GroupedNotificationEvents( 6 | val roomEvents: Map>>, 7 | val invitationEvents: List> 8 | ) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/provider/MatrixInstanceProvider.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.provider 2 | 3 | import org.matrix.android.sdk.api.Matrix 4 | 5 | 6 | object MatrixInstanceProvider { 7 | lateinit var matrix: Matrix 8 | private set 9 | 10 | fun saveMatrixInstance(matrixInstance: Matrix) { 11 | matrix = matrixInstance 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/timeline/post/create/PreviewPostListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.timeline.post.create 2 | 3 | import org.futo.circles.model.CreatePostContent 4 | 5 | interface PreviewPostListener { 6 | fun onUploadMediaClicked() 7 | fun onEmojiClicked() 8 | fun onAddLinkClicked() 9 | fun onSendClicked(content: CreatePostContent) 10 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/SelectRoomTypeArg.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | enum class SelectRoomTypeArg { 4 | CirclesJoined, 5 | GroupsJoined, 6 | PhotosJoined, 7 | MyCircleNotJoinedByUser 8 | } 9 | 10 | fun SelectRoomTypeArg.isCircle() = 11 | this == SelectRoomTypeArg.CirclesJoined || this == SelectRoomTypeArg.MyCircleNotJoinedByUser -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/PostItemPayload.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | import org.futo.circles.core.model.ReactionsData 4 | import org.matrix.android.sdk.api.session.room.send.SendState 5 | 6 | class PostItemPayload( 7 | val readByCount: Int, 8 | val repliesCount: Int, 9 | val reactions: List, 10 | val needToUpdateFullItem: Boolean 11 | ) -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/OtherEventContent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent 4 | 5 | data class OtherEventContent( 6 | val eventType: String, 7 | ) : PostContent(PostContentType.OTHER_CONTENT) 8 | 9 | fun TimelineEvent.toOtherEventContent(): OtherEventContent = OtherEventContent(root.getClearType()) -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_keyboard_arrow_down.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gallery/src/main/res/transition/grid_exit_transition.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/KeepInternalDistributor.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | 7 | 8 | class KeepInternalDistributor : BroadcastReceiver() { 9 | override fun onReceive(context: Context, intent: Intent) {} 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/menu/circles_tab_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/credentials/CredentialsManager.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.credentials 2 | 3 | import android.content.Context 4 | 5 | interface CredentialsManager { 6 | suspend fun getPasswordCredentials(activityContext: Context, userId: String): String? 7 | 8 | suspend fun savePasswordCredentials(activityContext: Context, userId: String, password: String) 9 | 10 | } -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/ValidateUserIdStatus.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | sealed class ValidateUserIdStatus 4 | 5 | data object EmptyUserId : ValidateUserIdStatus() 6 | data object InvalidUserId : ValidateUserIdStatus() 7 | data class ValidUserId(val userId: String) : ValidateUserIdStatus() 8 | data class SuggestedUserId(val suggestedUserId: String) : ValidateUserIdStatus() 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_rich_text_menu_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/DiscoveryResponse.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class DiscoveryResponse( 6 | @SerializedName("unifiedpush") val unifiedpush: DiscoveryUnifiedPush = DiscoveryUnifiedPush() 7 | ) 8 | 9 | data class DiscoveryUnifiedPush( 10 | @SerializedName("gateway") val gateway: String = "" 11 | ) -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /settings/src/main/java/org/futo/circles/settings/model/ActiveSubscriptionInfo.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.settings.model 2 | 3 | data class ActiveSubscriptionInfo( 4 | val packageName: String, 5 | val productId: String, 6 | val purchaseTime: Long, 7 | val isAutoRenewing: Boolean, 8 | val name: String, 9 | val description: String, 10 | val price: String, 11 | val duration: String 12 | ) -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/SwitchUserListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | import org.matrix.android.sdk.api.session.Session 5 | import org.matrix.android.sdk.api.session.user.model.User 6 | 7 | data class SwitchUserListItem( 8 | override val id: String, 9 | val session: Session, 10 | val user: User 11 | ) : IdEntity -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/AccessLevelListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | import org.matrix.android.sdk.api.session.room.powerlevels.Role 5 | 6 | data class AccessLevelListItem( 7 | val role: Role, 8 | val isSelected: Boolean = false 9 | ) : IdEntity { 10 | override val id: String = role.value.toString() 11 | } -------------------------------------------------------------------------------- /core/src/main/res/layout/list_item_invite_header.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /gallery/src/main/res/transition/image_shared_element_transition.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/timeline/post/menu/PostMenuListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.timeline.post.menu 2 | 3 | import org.futo.circles.core.model.PostContent 4 | 5 | 6 | interface PostMenuListener { 7 | fun onIgnore(senderId: String) 8 | fun onSaveToDevice(content: PostContent) 9 | fun onRemove(roomId: String, eventId: String) 10 | fun endPoll(roomId: String, eventId: String) 11 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_send.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/RoomRequestTypeArg.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | enum class RoomRequestTypeArg { Circle, Group, Photo, DM } 4 | 5 | 6 | fun RoomRequestTypeArg.toRoomTypeString() = when (this) { 7 | RoomRequestTypeArg.Circle -> TIMELINE_TYPE 8 | RoomRequestTypeArg.Group -> GROUP_TYPE 9 | RoomRequestTypeArg.Photo -> GALLERY_TYPE 10 | RoomRequestTypeArg.DM -> "" 11 | } -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_play_round.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_italic.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_error.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_reply.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /auth/src/main/res/layout/fragment_empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_error.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_resend.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_strikethrough.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_video.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/xml/provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 11 | 14 | 17 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/NotificationTestListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | import org.futo.circles.core.model.TaskStatus 5 | 6 | data class NotificationTestListItem( 7 | val titleId: Int, 8 | val message: String, 9 | val status: TaskStatus, 10 | val hasFix: Boolean 11 | ) : IdEntity { 12 | override val id: Int = titleId 13 | } -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_download.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_text.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_keyboard_arrow_up.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/xml/bg_chip.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/CreatePostContent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | import android.net.Uri 4 | import org.futo.circles.core.model.MediaType 5 | 6 | sealed class CreatePostContent 7 | 8 | data class TextPostContent( 9 | val text: String 10 | ) : CreatePostContent() 11 | 12 | data class MediaPostContent( 13 | val caption: String?, 14 | val uri: Uri, 15 | val mediaType: MediaType 16 | ) : CreatePostContent() 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #80000000 5 | #4D0E7AFE 6 | #00008b 7 | #52000000 8 | #C0E6FF 9 | #D5C4FF 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/org/futo/circles/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /settings/src/main/res/drawable/ic_email.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/menu/people_tab_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/feature/pass_phrase/recovery/EnterPassPhraseDialogListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.feature.pass_phrase.recovery 2 | 3 | import android.net.Uri 4 | 5 | interface EnterPassPhraseDialogListener { 6 | fun onRestoreBackupWithPassphrase(passphrase: String) 7 | 8 | fun onRestoreBackupWithRawKey(key: String) 9 | fun onRestoreBackup(uri: Uri) 10 | fun onDoNotRestore() 11 | fun onSelectFileClicked() 12 | } -------------------------------------------------------------------------------- /auth/src/test/java/org/futo/circles/auth/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/FcmHelper.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications 2 | 3 | interface FcmHelper { 4 | fun isFirebaseAvailable(): Boolean 5 | 6 | fun getFcmToken(): String? 7 | 8 | fun storeFcmToken(token: String?) 9 | 10 | fun ensureFcmTokenIsRetrieved(pushersManager: PushersManager, registerPusher: Boolean) 11 | 12 | fun onEnterForeground() 13 | 14 | fun onEnterBackground() 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/res/layout/fragment_pick_gallery.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/layout/list_item_invite_member.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/test/java/org/futo/circles/core/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /gallery/src/test/java/org/futo/circles/gallery/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.gallery 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /settings/src/main/res/drawable/ic_payment.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /settings/src/test/java/org/futo/circles/settings/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.settings 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PostContentType.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.matrix.android.sdk.api.session.room.model.message.MessageType 4 | 5 | enum class PostContentType(val typeKey: String) { 6 | TEXT_CONTENT(MessageType.MSGTYPE_TEXT), 7 | IMAGE_CONTENT(MessageType.MSGTYPE_IMAGE), 8 | VIDEO_CONTENT(MessageType.MSGTYPE_VIDEO), 9 | POLL_CONTENT(MessageType.MSGTYPE_POLL_START), 10 | OTHER_CONTENT("other_content") 11 | } -------------------------------------------------------------------------------- /core/src/main/res/menu/user_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | Circles combines the ease of use and convenience of an online social network with the privacy and security of an encrypted messenger. 2 | 3 | All posts are encrypted end-to-end, and there are no ads and no tracking. 4 | 5 | Connect with the people who truly matter in your life, without creepy ad networks spying on every interaction. 6 | 7 | Safely share photos of your kids with grandparents, aunts and uncles, friends and other family members. -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/circles/CirclesViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.circles 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.asLiveData 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import javax.inject.Inject 7 | 8 | @HiltViewModel 9 | class CirclesViewModel @Inject constructor( 10 | dataSource: CirclesDataSource 11 | ) : ViewModel() { 12 | 13 | val roomsLiveData = dataSource.getCirclesFlow().asLiveData() 14 | } -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/groups/GroupsViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.groups 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.asLiveData 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import javax.inject.Inject 7 | 8 | @HiltViewModel 9 | class GroupsViewModel @Inject constructor( 10 | dataSource: GroupsDataSource 11 | ) : ViewModel() { 12 | 13 | val roomsLiveData = dataSource.getGroupsFlow().asLiveData() 14 | 15 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/ShareUrlTypeArg.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | enum class ShareUrlTypeArg(val typeKey: String) { 4 | ROOM("room"), 5 | GALLERY("gallery"), 6 | GROUP("group"), 7 | TIMELINE("timeline") 8 | } 9 | 10 | fun shareUrlTypeArgFromType(type: String): ShareUrlTypeArg? { 11 | val urlType: ShareUrlTypeArg? = null 12 | ShareUrlTypeArg.entries.forEach { if (type == it.typeKey) return it } 13 | return urlType 14 | } -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_app_version.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_create.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/FileExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import androidx.core.content.FileProvider 6 | import org.futo.circles.core.base.FILE_PROVIDER_AUTHORITY_EXTENSION 7 | import java.io.File 8 | 9 | fun File.getUri(context: Context): Uri = FileProvider.getUriForFile( 10 | context, context.applicationContext.packageName + FILE_PROVIDER_AUTHORITY_EXTENSION, this 11 | ) -------------------------------------------------------------------------------- /core/src/main/res/values-night/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #64B5F6 4 | #333333 5 | #ABABAB 6 | #2D2D2D 7 | @color/menu_icon_color 8 | @color/white 9 | #121212 10 | -------------------------------------------------------------------------------- /gallery/src/main/java/org/futo/circles/gallery/feature/PhotosViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.gallery.feature 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.asLiveData 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import javax.inject.Inject 7 | 8 | @HiltViewModel 9 | class PhotosViewModel @Inject constructor( 10 | dataSource: PhotosDataSource 11 | ) : ViewModel() { 12 | 13 | val roomsLiveData = dataSource.getGalleriesFlow().asLiveData() 14 | } -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/direct/tab/DMViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.direct.tab 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.asLiveData 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import javax.inject.Inject 7 | 8 | @HiltViewModel 9 | class DMViewModel @Inject constructor( 10 | dataSource: DMDataSource 11 | ) : ViewModel() { 12 | 13 | val dmsLiveData = dataSource.getDirectMessagesListFlow().asLiveData() 14 | 15 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/picker/gallery/media/list/holder/GalleryTimelineItemViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.picker.gallery.media.list.holder 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.RecyclerView 5 | import org.futo.circles.core.model.GalleryTimelineListItem 6 | 7 | abstract class GalleryTimelineItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { 8 | 9 | abstract fun bind(item: GalleryTimelineListItem) 10 | 11 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PostContent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | 4 | sealed class PostContent(open val type: PostContentType) { 5 | fun isMedia(): Boolean = 6 | type == PostContentType.IMAGE_CONTENT || type == PostContentType.VIDEO_CONTENT 7 | 8 | fun isPoll(): Boolean = type == PostContentType.POLL_CONTENT 9 | 10 | fun isText(): Boolean = type == PostContentType.TEXT_CONTENT 11 | } 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/layout/list_item_no_results.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_more.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/ProcessedEvent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications 2 | 3 | data class ProcessedEvent( 4 | val type: Type, 5 | val event: T 6 | ) { 7 | 8 | enum class Type { 9 | KEEP, 10 | REMOVE 11 | } 12 | } 13 | 14 | fun List>.onlyKeptEvents() = mapNotNull { processedEvent -> 15 | processedEvent.event.takeIf { processedEvent.type == ProcessedEvent.Type.KEEP } 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/MediaFileData.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import android.net.Uri 4 | import org.matrix.android.sdk.api.session.crypto.attachments.ElementToDecrypt 5 | 6 | data class MediaFileData( 7 | val fileName: String, 8 | val mimeType: String, 9 | val fileUrl: String, 10 | val elementToDecrypt: ElementToDecrypt?, 11 | val width: Int, 12 | val height: Int, 13 | val duration: String, 14 | val videoUri: Uri? = null 15 | ) -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_fullscreen.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_poll.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_check_circle.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_direct_messages.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_gallery.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /gallery/src/main/res/drawable/ic_check_circle.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /auth/src/main/res/drawable/ic_file.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_phone.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_phone.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /settings/src/main/res/drawable/ic_key.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_logout.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /gallery/src/main/res/drawable/ic_create.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_image.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_number_list.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /auth/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/mapping/MatrixUserMapping.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.mapping 2 | 3 | import org.futo.circles.core.mapping.toCirclesUserSummary 4 | import org.futo.circles.model.PeopleIgnoredUserListItem 5 | import org.futo.circles.model.PeopleUserListItem 6 | import org.matrix.android.sdk.api.session.user.model.User 7 | 8 | 9 | fun User.toPeopleUserListItem(isIgnored: Boolean) = PeopleUserListItem(toCirclesUserSummary(), isIgnored) 10 | 11 | fun User.toPeopleIgnoredListItem() = PeopleIgnoredUserListItem(toCirclesUserSummary()) 12 | 13 | 14 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_invite.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_filter.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /core/src/main/res/layout/view_spinner_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/textDrawable/ColorGenerator.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.textDrawable 2 | 3 | import kotlin.math.abs 4 | 5 | class ColorGenerator { 6 | 7 | private val colors = mutableListOf( 8 | -0xe9c9c, 9 | -0xa7aa7, 10 | -0x65bc2, 11 | -0x1b39d2, 12 | -0x98408c, 13 | -0xa65d42, 14 | -0xdf6c33, 15 | -0x529d59, 16 | -0x7fa87f 17 | ) 18 | 19 | fun getColor(key: Any): Int { 20 | return colors[abs(key.hashCode()) % colors.size] 21 | } 22 | } -------------------------------------------------------------------------------- /core/src/main/res/layout/list_item_timeline_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /auth/src/main/res/drawable/ic_lightbulb.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/test/task/TestNotificationReceiver.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications.test.task 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import androidx.localbroadcastmanager.content.LocalBroadcastManager 7 | 8 | class TestNotificationReceiver : BroadcastReceiver() { 9 | 10 | override fun onReceive(context: Context, intent: Intent) { 11 | LocalBroadcastManager.getInstance(context).sendBroadcast(intent) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/room/manage_members/ManageMembersOptionsListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.room.manage_members 2 | 3 | import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent 4 | 5 | interface ManageMembersOptionsListener { 6 | fun onSetAccessLevel(userId: String, powerLevelsContent: PowerLevelsContent) 7 | fun onRemoveUser(userId: String) 8 | fun onBanUser(userId: String) 9 | fun unBanUser(userId: String) 10 | fun cancelPendingInvitation(userId: String) 11 | fun resendInvitation(userId: String) 12 | } -------------------------------------------------------------------------------- /gallery/src/main/res/drawable/ic_backup.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/NotificationActionIds.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications 2 | 3 | import org.futo.circles.core.base.CirclesAppConfig 4 | 5 | object NotificationActionIds { 6 | 7 | val markRoomRead = "${CirclesAppConfig.appId}.NotificationActions.MARK_ROOM_READ_ACTION" 8 | val dismissRoom = 9 | "${CirclesAppConfig.appId}.NotificationActions.DISMISS_ROOM_NOTIF_ACTION" 10 | val diagnostic = "${CirclesAppConfig.appId}.NotificationActions.DIAGNOSTIC" 11 | val push = "${CirclesAppConfig.appId}.PUSH" 12 | 13 | } -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_notifications.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_report.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_winner.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_emiji_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /core/src/main/res/layout/list_item_chip.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | -------------------------------------------------------------------------------- /settings/src/main/res/drawable/ic_verified.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/GalleryContentListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | 5 | sealed class GalleryTimelineListItem : IdEntity 6 | 7 | data class GalleryContentListItem( 8 | override val id: String, 9 | val postInfo: PostInfo, 10 | val mediaContent: MediaContent, 11 | val isSelected: Boolean = false 12 | ) : GalleryTimelineListItem() 13 | 14 | data class GalleryTimelineLoadingListItem( 15 | override val id: String = "GalleryTimelineLoadingListItem" 16 | ) : GalleryTimelineListItem() -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/timeline/data_source/ReadMessageDataSource.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.timeline.data_source 2 | 3 | import org.futo.circles.core.provider.MatrixSessionProvider 4 | import org.matrix.android.sdk.api.session.room.read.ReadService 5 | import javax.inject.Inject 6 | 7 | class ReadMessageDataSource @Inject constructor() { 8 | 9 | suspend fun markRoomAsRead(roomId: String) { 10 | MatrixSessionProvider.currentSession?.roomService()?.getRoom(roomId)?.readService() 11 | ?.markAsRead(ReadService.MarkAsReadParams.BOTH, false) 12 | } 13 | } -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/bsspeke/BSSpekeClientProvider.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.bsspeke 2 | 3 | object BSSpekeClientProvider { 4 | 5 | private var clientInstance: BSSpekeClient? = null 6 | 7 | fun getClientOrThrow() = 8 | clientInstance ?: throw IllegalArgumentException("BsSpeke client is not initialized") 9 | 10 | fun initClient(userPart: String, domain: String, password: String) { 11 | clientInstance = BSSpekeClient("@$userPart:$domain", domain, password) 12 | } 13 | 14 | fun clear() { 15 | clientInstance = null 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/NavControllerExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import android.net.Uri 4 | import androidx.annotation.IdRes 5 | import androidx.navigation.NavController 6 | import androidx.navigation.NavDirections 7 | import org.matrix.android.sdk.api.extensions.tryOrNull 8 | 9 | fun NavController.navigateSafe(directions: NavDirections) = tryOrNull { navigate(directions) } 10 | fun NavController.navigateSafe(@IdRes resId: Int) = tryOrNull { navigate(resId) } 11 | fun NavController.navigateSafe(deepLink: Uri) = tryOrNull { navigate(deepLink) } 12 | -------------------------------------------------------------------------------- /app/src/gplay/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/AccessLevel.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.base.READ_ONLY_ROLE 4 | import org.matrix.android.sdk.api.session.room.powerlevels.Role 5 | 6 | enum class AccessLevel(val levelValue: Int) { 7 | Admin(Role.Admin.value), 8 | Moderator(Role.Moderator.value), 9 | User(Role.Default.value), 10 | ReadOnly(READ_ONLY_ROLE); 11 | 12 | companion object { 13 | fun fromValue(value: Int): AccessLevel = 14 | AccessLevel.entries.firstOrNull { it.levelValue == value } ?: User 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_lock.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/subscriptions/SubscriptionManager.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.subscriptions 2 | 3 | import org.futo.circles.auth.model.SubscriptionListItem 4 | import org.futo.circles.auth.model.SubscriptionReceiptData 5 | import org.futo.circles.core.extensions.Response 6 | 7 | interface SubscriptionManager { 8 | 9 | suspend fun getActiveSubscriptionReceipt(): Response 10 | 11 | suspend fun getDetails(productIds: List): Response> 12 | 13 | suspend fun purchaseProduct(productId: String): Response 14 | 15 | } -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_seen.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /gallery/src/main/res/layout/fragment_select_galleries.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /gallery/src/main/res/menu/gallery_image_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | 18 | 19 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_lock_open.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/DMListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | import org.futo.circles.core.model.CirclesUserSummary 5 | 6 | sealed class DMListItem( 7 | override val id: String 8 | ) : IdEntity 9 | 10 | data class JoinedDMsListItem( 11 | override val id: String, 12 | val user: CirclesUserSummary, 13 | val timestamp: Long, 14 | val unreadCount: Int 15 | ) : DMListItem(id) 16 | 17 | data class DMsInvitesNotificationListItem( 18 | val invitesCount: Int 19 | ) : DMListItem("DirectMessagesInvitesNotificationListItem") -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bold.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_knock_requests.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_switch_user.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_deactivate_account.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_edit.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_reload.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/menu/timeline_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/picker/GetContentWithMultiFilter.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.picker 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import androidx.activity.result.contract.ActivityResultContracts 6 | 7 | class GetContentWithMultiFilter : ActivityResultContracts.GetContent() { 8 | override fun createIntent(context: Context, input: String): Intent { 9 | val inputArray = input.split(";").toTypedArray() 10 | val myIntent = super.createIntent(context, "*/*") 11 | myIntent.putExtra(Intent.EXTRA_MIME_TYPES, inputArray) 12 | return myIntent 13 | } 14 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/mapping/MatrixUserMapping.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.mapping 2 | 3 | import org.futo.circles.core.extensions.notEmptyDisplayName 4 | import org.futo.circles.core.model.CirclesUserSummary 5 | import org.futo.circles.core.model.UserListItem 6 | import org.matrix.android.sdk.api.session.user.model.User 7 | 8 | fun User.toUserListItem(isSelected: Boolean) = UserListItem( 9 | user = toCirclesUserSummary(), 10 | isSelected = isSelected 11 | ) 12 | 13 | fun User.toCirclesUserSummary() = CirclesUserSummary( 14 | id = userId, 15 | name = notEmptyDisplayName(), 16 | avatarUrl = avatarUrl ?: "" 17 | ) -------------------------------------------------------------------------------- /gallery/src/main/java/org/futo/circles/gallery/model/MediaToBackupItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.gallery.model 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import org.futo.circles.core.extensions.getUri 6 | import org.futo.circles.core.base.list.IdEntity 7 | import java.io.File 8 | 9 | data class MediaToBackupItem( 10 | val displayName: String, 11 | val uri: Uri, 12 | val size: Long, 13 | val dateModified: Long 14 | ) : IdEntity { 15 | override val id: Uri = uri 16 | } 17 | 18 | fun File.toMediaToBackupItem(context: Context) = MediaToBackupItem( 19 | name, getUri(context), length(), lastModified() 20 | ) -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_photo.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_circles_explanation.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/InviteNotifiableEvent.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | data class InviteNotifiableEvent( 4 | val matrixID: String?, 5 | override val eventId: String, 6 | override val editedEventId: String?, 7 | override val canBeReplaced: Boolean, 8 | val roomId: String, 9 | val roomName: String?, 10 | val noisy: Boolean, 11 | val title: String, 12 | val description: String, 13 | val type: String?, 14 | val timestamp: Long, 15 | val soundName: String?, 16 | override val isRedacted: Boolean = false, 17 | override val isUpdated: Boolean = false 18 | ) : NotifiableEvent -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/PostInfo.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.provider.MatrixSessionProvider 4 | import org.matrix.android.sdk.api.session.room.sender.SenderInfo 5 | 6 | data class PostInfo( 7 | val id: String, 8 | val roomId: String, 9 | val sender: SenderInfo, 10 | val isEncrypted: Boolean, 11 | val timestamp: Long, 12 | val isEdited: Boolean, 13 | val editTimestamp: Long? 14 | ) { 15 | fun isMyPost(): Boolean = 16 | sender.userId == MatrixSessionProvider.currentSession?.myUserId 17 | 18 | fun getLastModifiedTimestamp() = editTimestamp ?: timestamp 19 | } -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_add_photo.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /modules_release_clean.sh: -------------------------------------------------------------------------------- 1 | 2 | # Remove .aar and pom.xml from the root directory if they exist 3 | rm -f auth-gplay-release.aar 4 | rm -f auth-fdroid-release.aar 5 | 6 | rm -f core-gplay-release.aar 7 | rm -f core-fdroid-release.aar 8 | 9 | rm -f gallery-gplay-release.aar 10 | rm -f gallery-fdroid-release.aar 11 | 12 | rm -f settings-gplay-release.aar 13 | rm -f settings-fdroid-release.aar 14 | 15 | rm -f pom_auth_gplay.xml 16 | rm -f pom_auth_fdroid.xml 17 | 18 | rm -f pom_core_gplay.xml 19 | rm -f pom_core_fdroid.xml 20 | 21 | rm -f pom_gallery_gplay.xml 22 | rm -f pom_gallery_fdroid.xml 23 | 24 | rm -f pom_settings_gplay.xml 25 | rm -f pom_settings_fdroid.xml -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/CircleRoomTypeArg.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | enum class CircleRoomTypeArg { Circle, Group, Photo } 4 | 5 | fun CircleRoomTypeArg.toShareUrlType() = when (this) { 6 | CircleRoomTypeArg.Circle -> ShareUrlTypeArg.TIMELINE 7 | CircleRoomTypeArg.Group -> ShareUrlTypeArg.GROUP 8 | CircleRoomTypeArg.Photo -> ShareUrlTypeArg.GALLERY 9 | } 10 | 11 | fun CircleRoomTypeArg.toRoomRequestArgument() = when (this) { 12 | CircleRoomTypeArg.Circle -> RoomRequestTypeArg.Circle 13 | CircleRoomTypeArg.Group -> RoomRequestTypeArg.Group 14 | CircleRoomTypeArg.Photo -> RoomRequestTypeArg.Photo 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_item_report_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | -------------------------------------------------------------------------------- /settings/src/main/res/layout/activity_qr_scanner.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/utils/HomeServerUtils.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.utils 2 | 3 | import android.net.Uri 4 | import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig 5 | 6 | object HomeServerUtils { 7 | 8 | fun buildHomeServerConfigFromUserId(userId: String): HomeServerConnectionConfig { 9 | val domain = UserIdUtils.getServerDomain(userId) 10 | return buildHomeServerConfigFromDomain(domain) 11 | } 12 | 13 | 14 | fun buildHomeServerConfigFromDomain(domain: String) = HomeServerConnectionConfig 15 | .Builder() 16 | .withHomeServerUri(Uri.parse("https://$domain")) 17 | .build() 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_graph_bottom_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /core/src/fdroid/java/org/futo/circles/core/di/AppUpdateModule.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.di 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | import org.futo.circles.core.update.AppUpdateProvider 8 | import org.futo.circles.core.update.CirclesAppUpdateManager 9 | 10 | @Module 11 | @InstallIn(SingletonComponent::class) 12 | object AppUpdateModule { 13 | 14 | @Provides 15 | fun provideAppUpdateProvider(): AppUpdateProvider { 16 | return object : AppUpdateProvider { 17 | override fun getManager(): CirclesAppUpdateManager? = null 18 | } 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/RoomSummaryExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import org.futo.circles.core.mapping.nameOrId 4 | import org.futo.circles.core.model.RoomInfo 5 | import org.futo.circles.core.utils.getTimelineRoomFor 6 | import org.matrix.android.sdk.api.session.room.model.RoomSummary 7 | 8 | fun RoomSummary.toRoomInfo(isCircle: Boolean): RoomInfo = 9 | if (isCircle) { 10 | val timeline = getTimelineRoomFor(roomId)?.roomSummary() 11 | RoomInfo(timeline?.nameOrId() ?: nameOrId(), 12 | timeline?.avatarUrl?.takeIf { it.isNotEmpty() } ?: avatarUrl 13 | ) 14 | } else RoomInfo(nameOrId(), avatarUrl) -------------------------------------------------------------------------------- /app/src/fdroid/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bullet_list.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_help.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /auth/src/fdroid/java/org/futo/circles/auth/di/CredentialsModule.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.di 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | import org.futo.circles.auth.credentials.CredentialsManager 8 | import org.futo.circles.auth.credentials.CredentialsProvider 9 | 10 | @Module 11 | @InstallIn(SingletonComponent::class) 12 | object CredentialsModule { 13 | 14 | @Provides 15 | fun provideCredentialsProvider(): CredentialsProvider { 16 | return object : CredentialsProvider { 17 | override fun getManager(): CredentialsManager? = null 18 | } 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_curved_arrow.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_ignore.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_unfollow.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_photo.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/direct/timeline/listeners/DmOptionsListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.direct.timeline.listeners 2 | 3 | import org.futo.circles.core.model.PostContent 4 | 5 | interface DmOptionsListener { 6 | 7 | fun onShowMenuClicked(eventId: String) 8 | fun onShare(content: PostContent) 9 | fun onReply(message: String) 10 | fun onShowPreview(eventId: String) 11 | fun onShowEmoji(eventId: String, onAddEmoji: (String) -> Unit) 12 | fun onEmojiChipClicked(eventId: String, emoji: String, isUnSend: Boolean) 13 | fun onSaveToDevice(content: PostContent) 14 | fun onRemove(eventId: String) 15 | fun onEditActionClicked(eventId: String, message: String) 16 | 17 | } -------------------------------------------------------------------------------- /settings/src/main/res/drawable/ic_round_photo_library.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_contacts.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/SecretKeyData.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | import org.matrix.android.sdk.api.session.crypto.keysbackup.BackupRecoveryKey 4 | import org.matrix.android.sdk.api.session.crypto.keysbackup.computeRecoveryKey 5 | import org.matrix.android.sdk.api.session.securestorage.SsssKeySpec 6 | import org.matrix.android.sdk.api.util.fromBase64 7 | 8 | data class SecretKeyData( 9 | val keyId: String, 10 | val secretBase64: String, 11 | val keySpec: SsssKeySpec 12 | ) { 13 | fun getBackupRecoveryKey(): BackupRecoveryKey { 14 | val recoveryBase58 = computeRecoveryKey(secretBase64.fromBase64()) 15 | return BackupRecoveryKey.fromBase58(recoveryBase58) 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/base/ExpandableItemsDataSource.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.base 2 | 3 | import kotlinx.coroutines.flow.MutableStateFlow 4 | import kotlinx.coroutines.flow.update 5 | 6 | interface ExpandableItemsDataSource { 7 | 8 | val itemsWithVisibleOptionsFlow: MutableStateFlow> 9 | 10 | fun toggleOptionsVisibilityFor(id: String) { 11 | val isOptionsVisible = itemsWithVisibleOptionsFlow.value.contains(id) 12 | itemsWithVisibleOptionsFlow.update { value -> 13 | val newSet = mutableSetOf().apply { addAll(value) } 14 | if (isOptionsVisible) newSet.remove(id) 15 | else newSet.add(id) 16 | newSet 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/notifications/CircularCache.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.notifications 2 | 3 | class CircularCache(cacheSize: Int, factory: (Int) -> Array) { 4 | 5 | companion object { 6 | inline fun create(cacheSize: Int) = 7 | CircularCache(cacheSize) { Array(cacheSize) { null } } 8 | } 9 | 10 | private val cache = factory(cacheSize) 11 | private var writeIndex = 0 12 | 13 | fun contains(value: T): Boolean = cache.contains(value) 14 | 15 | fun put(value: T) { 16 | if (writeIndex == cache.size) { 17 | writeIndex = 0 18 | } 19 | cache[writeIndex] = value 20 | writeIndex++ 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_emoji.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/gplay/java/org/futo/circles/core/di/AppUpdateModule.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.di 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | import org.futo.circles.core.update.CirclesAppUpdateManager 8 | import org.futo.circles.core.update.AppUpdateProvider 9 | import org.futo.circles.core.update.GoogleAppUpdateManager 10 | 11 | @Module 12 | @InstallIn(SingletonComponent::class) 13 | object AppUpdateModule { 14 | 15 | @Provides 16 | fun provideAppUpdateProvider(): AppUpdateProvider { 17 | return object : AppUpdateProvider { 18 | override fun getManager(): CirclesAppUpdateManager = GoogleAppUpdateManager() 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /gallery/src/main/java/org/futo/circles/gallery/feature/select/SelectGalleriesViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.gallery.feature.select 2 | 3 | import androidx.lifecycle.ViewModel 4 | import dagger.hilt.android.lifecycle.HiltViewModel 5 | import org.futo.circles.core.model.SelectableRoomListItem 6 | import javax.inject.Inject 7 | 8 | @HiltViewModel 9 | class SelectGalleriesViewModel @Inject constructor( 10 | private val selectGalleriesDataSource: SelectGalleriesDataSource 11 | ) : ViewModel() { 12 | 13 | val galleriesLiveData = selectGalleriesDataSource.galleriesLiveData 14 | 15 | fun toggleGallerySelect(selectableRoomListItem: SelectableRoomListItem) { 16 | selectGalleriesDataSource.toggleGallerySelect(selectableRoomListItem) 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/room/select/SelectRoomsViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.room.select 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.asLiveData 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import org.futo.circles.core.model.SelectableRoomListItem 7 | import javax.inject.Inject 8 | 9 | @HiltViewModel 10 | class SelectRoomsViewModel @Inject constructor( 11 | private val dataSource: SelectRoomsDataSource 12 | ) : ViewModel() { 13 | 14 | val roomsLiveData = dataSource.roomsFlow.asLiveData() 15 | 16 | fun getSelectedRooms() = dataSource.getSelectedRooms() 17 | 18 | fun onRoomSelected(item: SelectableRoomListItem) { 19 | dataSource.toggleRoomSelect(item) 20 | } 21 | } -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_mention.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | @file:Suppress("UnstableApiUsage") 2 | 3 | import org.gradle.api.initialization.resolve.RepositoriesMode.FAIL_ON_PROJECT_REPOS 4 | 5 | pluginManagement { 6 | repositories { 7 | google() 8 | gradlePluginPortal() 9 | mavenCentral() 10 | mavenLocal() 11 | maven { url = uri("https://jitpack.io") } 12 | 13 | } 14 | } 15 | dependencyResolutionManagement { 16 | repositoriesMode.set(FAIL_ON_PROJECT_REPOS) 17 | repositories { 18 | google() 19 | mavenCentral() 20 | mavenLocal() 21 | maven { url = uri("https://jitpack.io") } 22 | } 23 | } 24 | 25 | include(":app") 26 | include(":auth") 27 | include(":core") 28 | include(":gallery") 29 | include(":settings") 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/GroupListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | import org.futo.circles.core.model.RoomInfo 5 | 6 | sealed class GroupListItem( 7 | override val id: String 8 | ) : IdEntity 9 | 10 | data class JoinedGroupListItem( 11 | override val id: String, 12 | val info: RoomInfo, 13 | val topic: String, 14 | val membersCount: Int, 15 | val knockRequestsCount: Int, 16 | val isEncrypted: Boolean, 17 | val timestamp: Long, 18 | val unreadCount: Int 19 | ) : GroupListItem(id) 20 | 21 | data class GroupInvitesNotificationListItem( 22 | val invitesCount: Int, 23 | val knockRequestsCount: Int 24 | ) : GroupListItem("GroupInvitesNotificationListItem") -------------------------------------------------------------------------------- /app/src/main/res/layout/view_people_search_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/utils/UserIdUtils.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.utils 2 | 3 | import org.futo.circles.core.provider.MatrixSessionProvider 4 | 5 | 6 | object UserIdUtils { 7 | 8 | fun removeDomainSuffix(userId: String): String { 9 | val serverDomain = MatrixSessionProvider.currentSession?.sessionParams?.homeServerHost ?: "" 10 | val shortUserId: String = if (userId.endsWith(":$serverDomain")) 11 | userId.removeSuffix(":$serverDomain") 12 | else { 13 | val shortDomain = serverDomain.substringAfter("matrix.") 14 | userId.removeSuffix(":$shortDomain") 15 | } 16 | return shortUserId 17 | } 18 | 19 | fun getServerDomain(userId: String) = userId.substringAfter(":") 20 | 21 | } -------------------------------------------------------------------------------- /gallery/src/main/java/org/futo/circles/gallery/feature/share/UploadToGalleryActivity.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.gallery.feature.share 2 | 3 | import dagger.hilt.android.AndroidEntryPoint 4 | import org.futo.circles.core.feature.room.select.interfaces.RoomsPicker 5 | import org.futo.circles.core.feature.share.BaseShareActivity 6 | import org.futo.circles.gallery.R 7 | import org.futo.circles.gallery.feature.select.SelectGalleriesFragment 8 | 9 | @AndroidEntryPoint 10 | class UploadToGalleryActivity : BaseShareActivity() { 11 | 12 | override val titleResId: Int = R.string.upload_to_gallery 13 | override val roomsPicker: RoomsPicker = SelectGalleriesFragment() 14 | 15 | override fun getShareRoomsIds(): List = roomsPicker.getSelectedRooms().map { it.id } 16 | 17 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/picker/gallery/rooms/PickGalleryViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.picker.gallery.rooms 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.map 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import org.futo.circles.core.mapping.toJoinedGalleryListItem 7 | import org.futo.circles.core.utils.getGalleriesLiveData 8 | import org.matrix.android.sdk.api.session.room.model.Membership 9 | import javax.inject.Inject 10 | 11 | @HiltViewModel 12 | class PickGalleryViewModel @Inject constructor() : ViewModel() { 13 | 14 | val galleriesLiveData = getGalleriesLiveData(membershipFilter = listOf(Membership.JOIN)) 15 | .map { galleries -> galleries.map { it.toJoinedGalleryListItem() } } 16 | 17 | } -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/timeline/list/PostOptionsListener.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.timeline.list 2 | 3 | import android.view.View 4 | import org.futo.circles.core.model.PostContent 5 | 6 | 7 | interface PostOptionsListener { 8 | fun onShowMenuClicked(roomId: String, eventId: String) 9 | fun onUserClicked(userId: String) 10 | fun onShare(content: PostContent, view: View) 11 | fun onReply(roomId: String, eventId: String) 12 | fun onShowPreview(roomId: String, eventId: String) 13 | fun onShowEmoji(roomId: String, eventId: String, onAddEmoji: (String) -> Unit) 14 | fun onEmojiChipClicked(roomId: String, eventId: String, emoji: String, isUnSend: Boolean) 15 | fun onPollOptionSelected(roomId: String, eventId: String, optionId: String) 16 | } -------------------------------------------------------------------------------- /auth/src/gplay/java/org/futo/circles/auth/di/CredentialsModule.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.di 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.components.SingletonComponent 7 | import org.futo.circles.auth.credentials.CredentialsManager 8 | import org.futo.circles.auth.credentials.CredentialsProvider 9 | import org.futo.circles.auth.credentials.GoogleCredentialsManager 10 | 11 | @Module 12 | @InstallIn(SingletonComponent::class) 13 | object CredentialsModule { 14 | 15 | @Provides 16 | fun provideCredentialsProvider(): CredentialsProvider { 17 | return object : CredentialsProvider { 18 | override fun getManager(): CredentialsManager = GoogleCredentialsManager() 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /settings/src/main/res/drawable/ic_unverified.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/Post.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | import org.matrix.android.sdk.api.session.room.send.SendState 5 | 6 | sealed class PostListItem : IdEntity 7 | data class TimelineLoadingItem( 8 | override val id: String = "TimelineLoadingItem" 9 | ) : PostListItem() 10 | 11 | data class Post( 12 | val postInfo: PostInfo, 13 | val content: PostContent, 14 | val readByCount: Int, 15 | val repliesCount: Int, 16 | val reactionsData: List, 17 | val timelineName: String? = null, 18 | val timelineOwnerName: String? = null 19 | ) : PostListItem() { 20 | override val id: String get() = postInfo.id 21 | fun isMyPost(): Boolean = postInfo.isMyPost() 22 | } -------------------------------------------------------------------------------- /core/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/NotificationAction.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.matrix.android.sdk.api.session.pushrules.Action 4 | 5 | data class NotificationAction( 6 | val shouldNotify: Boolean, 7 | val highlight: Boolean, 8 | val soundName: String? 9 | ) 10 | 11 | fun List.toNotificationAction(): NotificationAction { 12 | var shouldNotify = false 13 | var highlight = false 14 | var sound: String? = null 15 | forEach { action -> 16 | when (action) { 17 | is Action.Notify -> shouldNotify = true 18 | is Action.Highlight -> highlight = action.highlight 19 | is Action.Sound -> sound = action.sound 20 | } 21 | } 22 | return NotificationAction(shouldNotify, highlight, sound) 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/res/layout/dialog_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/user/UserOptionsDataSource.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.user 2 | 3 | import org.futo.circles.core.extensions.createResult 4 | import org.futo.circles.core.provider.MatrixSessionProvider 5 | import javax.inject.Inject 6 | 7 | class UserOptionsDataSource @Inject constructor() { 8 | 9 | val ignoredUsersLiveData = 10 | MatrixSessionProvider.currentSession?.userService()?.getIgnoredUsersLive() 11 | 12 | suspend fun ignoreSender(userId: String) = createResult { 13 | MatrixSessionProvider.currentSession?.userService()?.ignoreUserIds(listOf(userId)) 14 | } 15 | 16 | suspend fun unIgnoreSender(userId: String) = createResult { 17 | MatrixSessionProvider.currentSession?.userService()?.unIgnoreUserIds(listOf(userId)) 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/ResLoadingData.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import androidx.annotation.StringRes 4 | import org.futo.circles.core.R 5 | 6 | sealed class LoadingData( 7 | open var progress: Int, 8 | open var total: Int, 9 | open var isLoading: Boolean 10 | ) 11 | 12 | data class ResLoadingData( 13 | @StringRes var messageId: Int = R.string.loading, 14 | override var progress: Int = 0, 15 | override var total: Int = 0, 16 | override var isLoading: Boolean = true 17 | ) : LoadingData(progress, total, isLoading) 18 | 19 | data class MessageLoadingData( 20 | var message: String, 21 | override var progress: Int = 0, 22 | override var total: Int = 0, 23 | override var isLoading: Boolean = true 24 | ) : LoadingData(progress, total, isLoading) -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/ViewModelExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import kotlinx.coroutines.* 6 | import kotlin.coroutines.CoroutineContext 7 | 8 | private val mainContext: CoroutineContext = Dispatchers.Main 9 | private val ioContext: CoroutineContext = Dispatchers.IO 10 | 11 | fun ViewModel.launchUi( 12 | block: suspend CoroutineScope.() -> Unit 13 | ): Job = viewModelScope.launch(mainContext + defaultExceptionHandler()) { this.block() } 14 | 15 | fun ViewModel.launchBg( 16 | exceptionHandler: CoroutineExceptionHandler = defaultExceptionHandler(), 17 | block: suspend CoroutineScope.() -> Unit 18 | ): Job = viewModelScope.launch(ioContext + exceptionHandler) { this.block() } 19 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_notifications_off.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/navigation/filter_timelines_nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /auth/src/androidTest/java/org/futo/circles/auth/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("org.futo.circles.auth.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/ConfirmationType.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | import org.futo.circles.auth.R 4 | import org.futo.circles.core.model.ConfirmationType 5 | 6 | data class RemoveUser( 7 | override val titleRes: Int = org.futo.circles.core.R.string.remove_user, 8 | override val messageRes: Int = org.futo.circles.core.R.string.remove_user_message, 9 | override val positiveButtonRes: Int = org.futo.circles.core.R.string.remove 10 | ) : ConfirmationType(titleRes, messageRes, positiveButtonRes) 11 | 12 | data class ForgotPassword( 13 | override val titleRes: Int = R.string.forgot_password, 14 | override val messageRes: Int = R.string.forgot_password_message, 15 | override val positiveButtonRes: Int = R.string.confirm 16 | ) : ConfirmationType(titleRes, messageRes, positiveButtonRes) -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/model/CustomUIAuth.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.model 2 | 3 | import org.futo.circles.auth.feature.uia.UIADataSource.Companion.USER_PARAM_KEY 4 | import org.futo.circles.core.provider.MatrixSessionProvider 5 | import org.matrix.android.sdk.api.auth.UIABaseAuth 6 | import org.matrix.android.sdk.api.util.JsonDict 7 | 8 | data class CustomUIAuth( 9 | override val session: String, 10 | val auth: JsonDict 11 | ) : UIABaseAuth { 12 | override fun hasAuthInfo() = true 13 | 14 | override fun copyWithSession(session: String) = this.copy(session = session) 15 | 16 | override fun asMap(): Map = auth.toMutableMap().apply { 17 | this["session"] = session 18 | this[USER_PARAM_KEY] = MatrixSessionProvider.currentSession?.myUserId ?: "" 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /core/src/androidTest/java/org/futo/circles/core/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("org.futo.circles.core.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/markdown/span/MentionSpan.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.markdown.span 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.Drawable 5 | import android.text.style.DynamicDrawableSpan 6 | import androidx.core.content.ContextCompat 7 | import com.google.android.material.chip.ChipDrawable 8 | import org.futo.circles.core.R 9 | 10 | class MentionSpan( 11 | private val context: Context, 12 | val name: String 13 | ) : DynamicDrawableSpan() { 14 | 15 | override fun getDrawable(): Drawable = 16 | ChipDrawable.createFromResource(context, R.xml.bg_chip).apply { 17 | setTextColor(ContextCompat.getColor(context, R.color.blue)) 18 | text = name 19 | setBounds(0, 0, intrinsicWidth, intrinsicHeight) 20 | } 21 | } -------------------------------------------------------------------------------- /core/src/main/res/navigation/create_room_nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/timeline/list/holder/TimelineLoadingViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.timeline.list.holder 2 | 3 | import android.view.ViewGroup 4 | import org.futo.circles.core.base.list.ViewBindingHolder 5 | import org.futo.circles.core.databinding.ListItemTimelineLoadingBinding 6 | import org.futo.circles.core.model.PostListItem 7 | import org.futo.circles.core.model.TimelineLoadingItem 8 | import org.futo.circles.feature.timeline.base.TimelineListItemViewHolder 9 | 10 | class TimelineLoadingViewHolder( 11 | parent: ViewGroup, 12 | ) : TimelineListItemViewHolder(inflate(parent, ListItemTimelineLoadingBinding::inflate)) { 13 | 14 | private companion object : ViewBindingHolder 15 | 16 | override fun bind(item: PostListItem) { 17 | if (item !is TimelineLoadingItem) return 18 | } 19 | } -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/feature/uia/UIADataSourceProvider.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.feature.uia 2 | 3 | import org.futo.circles.auth.model.UIAFlowType 4 | 5 | 6 | object UIADataSourceProvider { 7 | 8 | private var activeFlowDataSource: UIADataSource? = null 9 | 10 | var activeFlowType: UIAFlowType? = null 11 | private set 12 | 13 | fun getDataSourceOrThrow() = 14 | activeFlowDataSource ?: throw IllegalArgumentException("Flow is not active") 15 | 16 | 17 | fun create(flowType: UIAFlowType, factory: UIADataSource.Factory): UIADataSource { 18 | activeFlowType = flowType 19 | return factory.create(flowType).also { activeFlowDataSource = it } 20 | } 21 | 22 | fun clear() { 23 | activeFlowType = null 24 | activeFlowDataSource = null 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/subscriptions/IsoPeriod.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.subscriptions 2 | 3 | import android.content.Context 4 | import org.futo.circles.auth.R 5 | 6 | fun String.formatIsoPeriod(context: Context): String = toDurationNumberPairs() 7 | .joinToString(separator = " ") { (number, duration) -> 8 | context.resources.getQuantityString( 9 | when (duration) { 10 | "D" -> R.plurals.days 11 | "W" -> R.plurals.weeks 12 | "M" -> R.plurals.months 13 | "Y" -> R.plurals.years 14 | 15 | else -> R.plurals.days 16 | }, number, number 17 | ) 18 | } 19 | 20 | private fun String.toDurationNumberPairs() = removePrefix("P") 21 | .chunked(2) 22 | .map { it[0].toString().toInt() to it[1].toString() } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/model/FilterTimelinesListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | import org.futo.circles.core.extensions.getRoomOwner 5 | import org.futo.circles.core.mapping.nameOrId 6 | import org.matrix.android.sdk.api.session.room.model.RoomSummary 7 | 8 | data class FilterTimelinesListItem( 9 | override val id: String, 10 | val name: String, 11 | val ownerName: String, 12 | val avatarUrl: String, 13 | val isSelected: Boolean 14 | ) : IdEntity 15 | 16 | fun RoomSummary.toFilterTimelinesListItem(isSelected: Boolean = true) = FilterTimelinesListItem( 17 | id = roomId, 18 | name = nameOrId(), 19 | ownerName = getRoomOwner(roomId)?.displayName ?: "", 20 | avatarUrl = avatarUrl, 21 | isSelected = isSelected 22 | ) -------------------------------------------------------------------------------- /auth/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 -------------------------------------------------------------------------------- /core/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 -------------------------------------------------------------------------------- /auth/src/main/java/org/futo/circles/auth/feature/pass_phrase/EncryptionAlgorithmHelper.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.auth.feature.pass_phrase 2 | 3 | import org.futo.circles.core.provider.MatrixSessionProvider 4 | import org.matrix.android.sdk.api.crypto.BSSPEKE_ALGORITHM_BACKUP 5 | import org.matrix.android.sdk.api.session.securestorage.KeyInfoResult 6 | import javax.inject.Inject 7 | 8 | class EncryptionAlgorithmHelper @Inject constructor() { 9 | 10 | fun isBsSpekePassPhrase(): Boolean { 11 | val keyInfoResult = MatrixSessionProvider.getSessionOrThrow() 12 | .sharedSecretStorageService().getDefaultKey() 13 | if (!keyInfoResult.isSuccess()) return false 14 | val algo = (keyInfoResult as KeyInfoResult.Success).keyInfo.content.passphrase?.algorithm 15 | return algo == BSSPEKE_ALGORITHM_BACKUP 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /gallery/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.kts. 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 -------------------------------------------------------------------------------- /settings/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.kts. 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 -------------------------------------------------------------------------------- /settings/src/androidTest/java/org/futo/circles/settings/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.settings 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("org.futo.circles.settings.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/share/group/ShareWithGroupActivity.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.share.group 2 | 3 | import dagger.hilt.android.AndroidEntryPoint 4 | import org.futo.circles.R 5 | import org.futo.circles.core.feature.room.select.SelectRoomsFragment 6 | import org.futo.circles.core.feature.room.select.interfaces.RoomsPicker 7 | import org.futo.circles.core.feature.share.BaseShareActivity 8 | import org.futo.circles.core.model.SelectRoomTypeArg 9 | 10 | @AndroidEntryPoint 11 | class ShareWithGroupActivity : BaseShareActivity() { 12 | 13 | override val titleResId: Int = R.string.share_with_group 14 | 15 | override val roomsPicker: RoomsPicker = 16 | SelectRoomsFragment.create(SelectRoomTypeArg.GroupsJoined) 17 | 18 | override fun getShareRoomsIds(): List = roomsPicker.getSelectedRooms().map { it.id } 19 | 20 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/picker/gallery/media/list/holder/GalleryTimelineLoadingViewHolder.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.picker.gallery.media.list.holder 2 | 3 | import android.view.ViewGroup 4 | import org.futo.circles.core.base.list.ViewBindingHolder 5 | import org.futo.circles.core.databinding.ListItemTimelineLoadingBinding 6 | import org.futo.circles.core.model.GalleryTimelineListItem 7 | import org.futo.circles.core.model.GalleryTimelineLoadingListItem 8 | 9 | 10 | class GalleryTimelineLoadingViewHolder( 11 | parent: ViewGroup, 12 | ) : GalleryTimelineItemViewHolder(inflate(parent, ListItemTimelineLoadingBinding::inflate)) { 13 | 14 | private companion object : ViewBindingHolder 15 | 16 | override fun bind(item: GalleryTimelineListItem) { 17 | if (item !is GalleryTimelineLoadingListItem) return 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/base/Constants.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.base 2 | 3 | import org.futo.circles.core.provider.MatrixSessionProvider 4 | 5 | const val FILE_PROVIDER_AUTHORITY_EXTENSION = ".provider" 6 | const val MediaCaptionFieldKey = "caption" 7 | const val READ_ONLY_ROLE = -10 8 | 9 | val PUSHER_APP_ID = "${CirclesAppConfig.appId}.android" 10 | 11 | fun getPusherUrl(): String = "https://sygnal.${getCirclesDomain()}/_matrix/push/v1/notify" 12 | 13 | const val DEFAULT_PUSH_GATEWAY = "https://matrix.gateway.unifiedpush.org/_matrix/push/v1/notify" 14 | 15 | fun getCirclesDomain(): String { 16 | val homeServerUrl = MatrixSessionProvider.currentSession?.sessionParams?.homeServerUrl ?: "" 17 | return CirclesAppConfig.serverDomains().firstOrNull { homeServerUrl.contains(it) } 18 | ?: CirclesAppConfig.serverDomains().first() 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/SearchViewExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import androidx.appcompat.widget.SearchView 4 | import kotlinx.coroutines.flow.MutableStateFlow 5 | import kotlinx.coroutines.flow.StateFlow 6 | 7 | fun SearchView.getQueryTextChangeStateFlow(onTextChanged: ((String) -> Unit)? = null): StateFlow { 8 | 9 | val query = MutableStateFlow("") 10 | 11 | setOnQueryTextListener(object : SearchView.OnQueryTextListener { 12 | override fun onQueryTextSubmit(query: String?): Boolean { 13 | return true 14 | } 15 | 16 | override fun onQueryTextChange(newText: String): Boolean { 17 | onTextChanged?.invoke(newText) 18 | query.value = newText 19 | return true 20 | } 21 | }) 22 | 23 | return query 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_ban.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/res/drawable/ic_person_remove.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/base/SingleEventLiveData.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.base 2 | 3 | import androidx.annotation.MainThread 4 | import androidx.lifecycle.LifecycleOwner 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.Observer 7 | import java.util.concurrent.atomic.AtomicBoolean 8 | 9 | 10 | class SingleEventLiveData : MutableLiveData() { 11 | private val pending = AtomicBoolean(false) 12 | 13 | @MainThread 14 | override fun observe(owner: LifecycleOwner, observer: Observer) { 15 | super.observe(owner) { t -> 16 | if (pending.compareAndSet(true, false)) { 17 | observer.onChanged(t) 18 | } 19 | } 20 | } 21 | 22 | @MainThread 23 | override fun setValue(t: T?) { 24 | pending.set(true) 25 | super.setValue(t) 26 | } 27 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/MatrixUserExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import org.futo.circles.core.utils.UserIdUtils 4 | import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary 5 | import org.matrix.android.sdk.api.session.room.sender.SenderInfo 6 | import org.matrix.android.sdk.api.session.user.model.User 7 | 8 | 9 | fun User.notEmptyDisplayName(): String = getName(userId, displayName) 10 | 11 | fun RoomMemberSummary.notEmptyDisplayName(): String = getName(userId, displayName) 12 | 13 | fun SenderInfo.notEmptyDisplayName(): String = getName(userId, displayName) 14 | 15 | private fun getName(userId: String, displayName: String?): String { 16 | val name = displayName?.takeIf { it.isNotEmpty() } 17 | ?: userId.replace("@", "").substringBefore(":") 18 | return UserIdUtils.removeDomainSuffix(name) 19 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/TimelineEventExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import org.futo.circles.core.model.PostContentType 4 | import org.matrix.android.sdk.api.session.events.model.EventType 5 | import org.matrix.android.sdk.api.session.events.model.toModel 6 | import org.matrix.android.sdk.api.session.room.model.message.MessageContent 7 | import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent 8 | import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent 9 | 10 | 11 | fun TimelineEvent.getPostContentType(): PostContentType? { 12 | val messageType = if (root.getClearType() == EventType.MESSAGE) root.getClearContent() 13 | .toModel()?.msgType 14 | else getLastMessageContent()?.msgType 15 | return PostContentType.entries.firstOrNull { it.typeKey == messageType } 16 | } -------------------------------------------------------------------------------- /core/src/main/res/layout/list_item_access_level.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /core/src/main/res/layout/list_item_invite_notification.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | -------------------------------------------------------------------------------- /settings/src/main/java/org/futo/circles/settings/view/EditEmailView.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.settings.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import androidx.constraintlayout.widget.ConstraintLayout 7 | import org.futo.circles.settings.databinding.ViewEditEmailBinding 8 | 9 | 10 | class EditEmailView( 11 | context: Context, 12 | attrs: AttributeSet? = null, 13 | ) : ConstraintLayout(context, attrs) { 14 | 15 | private val binding = 16 | ViewEditEmailBinding.inflate(LayoutInflater.from(context), this) 17 | 18 | fun setData( 19 | email: String, 20 | onRemove: (String) -> Unit, 21 | ) { 22 | with(binding) { 23 | tvEmail.text = email 24 | binding.ivRemove.setOnClickListener { onRemove.invoke(email) } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/feature/splash/SplashFragment.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.feature.splash 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.Fragment 6 | import androidx.navigation.fragment.findNavController 7 | import org.futo.circles.R 8 | import org.futo.circles.core.extensions.navigateSafe 9 | import org.futo.circles.core.provider.MatrixSessionProvider 10 | 11 | class SplashFragment : Fragment(R.layout.fragment_splash) { 12 | 13 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 14 | super.onViewCreated(view, savedInstanceState) 15 | val destination = MatrixSessionProvider.currentSession?.let { 16 | SplashFragmentDirections.toHomeFragment() 17 | } ?: SplashFragmentDirections.toLogInFragment() 18 | 19 | findNavController().navigateSafe(destination) 20 | } 21 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/glide/LocalFileHelper.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.glide 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import androidx.documentfile.provider.DocumentFile 6 | import org.matrix.android.sdk.api.extensions.orFalse 7 | import java.io.InputStream 8 | 9 | class LocalFileHelper(private val context: Context) { 10 | 11 | fun isLocalFile(fileUri: String?): Boolean { 12 | return fileUri 13 | ?.let { Uri.parse(it) } 14 | ?.let { DocumentFile.fromSingleUri(context, it) } 15 | ?.exists() 16 | .orFalse() 17 | } 18 | 19 | fun openInputStream(fileUri: String?): InputStream? { 20 | return fileUri 21 | ?.takeIf { isLocalFile(it) } 22 | ?.let { Uri.parse(it) } 23 | ?.let { context.contentResolver.openInputStream(it) } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/view/NetworkRequiredButton.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.View 6 | import android.view.View.OnClickListener 7 | import com.google.android.material.button.MaterialButton 8 | import org.futo.circles.core.base.NetworkObserver 9 | 10 | class NetworkRequiredButton( 11 | context: Context, 12 | attrs: AttributeSet? = null 13 | ) : MaterialButton(context, attrs), OnClickListener { 14 | 15 | private var customClickListener: OnClickListener? = null 16 | 17 | override fun setOnClickListener(l: OnClickListener?) { 18 | customClickListener = l 19 | super.setOnClickListener(this) 20 | } 21 | 22 | override fun onClick(v: View?) { 23 | if (!NetworkObserver.isConnected()) return 24 | customClickListener?.onClick(v) 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/org/futo/circles/model/CircleListItem.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.model 2 | 3 | import org.futo.circles.core.base.list.IdEntity 4 | import org.futo.circles.core.model.RoomInfo 5 | 6 | sealed class CircleListItem : IdEntity 7 | data class CirclesHeaderItem( 8 | val titleRes: Int 9 | ) : CircleListItem() { 10 | override val id: String = titleRes.toString() 11 | } 12 | 13 | data class JoinedCircleListItem( 14 | override val id: String, 15 | val timelineId: String, 16 | val info: RoomInfo, 17 | val followingCount: Int, 18 | val followedByCount: Int, 19 | val unreadCount: Int, 20 | val knockRequestsCount: Int 21 | ) : CircleListItem() 22 | 23 | data class CircleInvitesNotificationListItem( 24 | val invitesCount: Int, 25 | val knocksCount: Int 26 | ) : CircleListItem() { 27 | override val id: String = "CircleInvitesNotificationListItem" 28 | } 29 | 30 | -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/extensions/MediaContentDataExtensions.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.extensions 2 | 3 | import android.util.Size 4 | import android.widget.ImageView 5 | import org.futo.circles.core.model.MediaContent 6 | import org.futo.circles.core.model.MediaFileData 7 | 8 | fun MediaContent.loadEncryptedThumbOrFullIntoWithAspect(imageView: ImageView) { 9 | val fileContent = thumbnailFileData ?: mediaFileData 10 | fileContent.loadEncryptedIntoWithAspect(imageView, thumbHash) 11 | } 12 | 13 | fun MediaFileData.loadEncryptedIntoWithAspect( 14 | imageView: ImageView, 15 | thumbHash: String? = null 16 | ) { 17 | imageView.post { 18 | if (fileUrl.startsWith(UriContentScheme)) { 19 | imageView.loadImage(fileUrl) 20 | } else { 21 | imageView.loadEncryptedImage(this, Size(width, height), thumbHash = thumbHash) 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/circles/following/list/FollowingAdapter.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.circles.following.list 2 | 3 | import android.view.ViewGroup 4 | import org.futo.circles.core.base.list.BaseRvAdapter 5 | import org.futo.circles.core.model.FollowingListItem 6 | 7 | class FollowingAdapter( 8 | private val onRemoveClicked: (FollowingListItem) -> Unit 9 | ) : BaseRvAdapter(DefaultIdEntityCallback()) { 10 | 11 | override fun onCreateViewHolder( 12 | parent: ViewGroup, 13 | viewType: Int 14 | ): FollowingViewHolder = FollowingViewHolder( 15 | parent = parent, 16 | onRemoveClicked = { position -> onRemoveClicked(getItem(position)) } 17 | ) 18 | 19 | override fun onBindViewHolder(holder: FollowingViewHolder, position: Int) { 20 | holder.bind(getItem(position)) 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /core/src/main/java/org/futo/circles/core/feature/room/select/list/SelectRoomsAdapter.kt: -------------------------------------------------------------------------------- 1 | package org.futo.circles.core.feature.room.select.list 2 | 3 | import android.view.ViewGroup 4 | import org.futo.circles.core.base.list.BaseRvAdapter 5 | import org.futo.circles.core.model.SelectableRoomListItem 6 | 7 | class SelectRoomsAdapter( 8 | private val onRoomSelected: (SelectableRoomListItem) -> Unit 9 | ) : BaseRvAdapter(DefaultIdEntityCallback()) { 10 | 11 | override fun onCreateViewHolder( 12 | parent: ViewGroup, 13 | viewType: Int 14 | ): SelectRoomsViewHolder = SelectRoomsViewHolder( 15 | parent, 16 | onCircleClicked = { position -> onRoomSelected(getItem(position)) }) 17 | 18 | 19 | override fun onBindViewHolder(holder: SelectRoomsViewHolder, position: Int) { 20 | holder.bind(getItem(position)) 21 | } 22 | 23 | } --------------------------------------------------------------------------------