├── .aiexclude ├── .buildkite ├── commands │ ├── lint.sh │ ├── prototype-build.sh │ └── publish-to-maven.sh ├── pipeline.yml └── shared-pipeline-vars ├── .bundle └── config ├── .configure ├── .configure-files └── secrets.properties.enc ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── enhancement.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── docs.yml │ ├── run-danger.yml │ └── submit-gradle-dependencies.yml ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── checkstyle-idea.xml ├── codeStyles ├── compiler.xml ├── encodings.xml └── migrations.xml ├── .java-version ├── .rubocop.yml ├── .ruby-version ├── CODE-OF-CONDUCT.md ├── CONTRIBUTING.md ├── Dangerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE.md ├── README.md ├── build-logic ├── convention │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── GravatarAndroidLibraryConventionPlugin.kt │ │ ├── GravatarComposeConventionPlugin.kt │ │ ├── GravatarOpenApiGeneratorConventionPlugin.kt │ │ ├── MavenPublishConventionPlugin.kt │ │ └── com │ │ └── gravatar │ │ ├── Detekt.kt │ │ ├── KotlinAndroid.kt │ │ └── ProjectExtensions.kt ├── gradle.properties └── settings.gradle.kts ├── build.gradle.kts ├── compose_compiler_config.conf ├── config └── detekt │ └── detekt.yml ├── demo-app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── gravatar │ │ │ └── demoapp │ │ │ ├── DemoFileProvider.kt │ │ │ ├── MainActivity.kt │ │ │ ├── theme │ │ │ ├── Color.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ │ │ └── ui │ │ │ ├── AvatarUpdateTab.kt │ │ │ ├── DemoGravatarApp.kt │ │ │ ├── DemoProfileSummaryCard.kt │ │ │ ├── DropdownMenuWithCheckbox.kt │ │ │ ├── GravatarImageSettings.kt │ │ │ ├── activity │ │ │ └── QuickEditorTestActivity.kt │ │ │ ├── components │ │ │ ├── AboutFieldsBottomSheet.kt │ │ │ ├── ExpandableSection.kt │ │ │ ├── GravatarEmailInput.kt │ │ │ └── GravatarPasswordInput.kt │ │ │ ├── model │ │ │ └── SettingsState.kt │ │ │ └── utils │ │ │ └── UtilExtensions.kt │ └── res │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── ic_launcher_foreground.xml │ │ ├── layout │ │ └── activity_quick_editor_test.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_background.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_background.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_background.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_background.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_background.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ ├── data_extraction_rules.xml │ │ ├── filepaths.xml │ │ └── network_security_config.xml │ └── test │ └── java │ └── com │ └── gravatar │ └── lib │ └── ExampleUnitTest.kt ├── docs ├── get-started │ ├── get-started.md │ ├── screenshot-developers-portal.png │ ├── usage-core.md │ ├── usage-quickeditor.md │ └── usage-ui.md ├── images │ ├── about_editor_all_fields.png │ ├── about_editor_two_fields.png │ ├── avatar_and_about.gif │ ├── avatar_picker_horizontal.png │ ├── avatar_picker_vertical.png │ ├── dokka │ │ └── logo-icon.svg │ ├── gravatar_logo.png │ ├── large_profile.png │ ├── large_profile_summary.png │ ├── profile_light_demo.png │ ├── profile_summary.png │ └── redundant_visibility_modifier_warning.png ├── sdk-technical-details │ └── sdk-techincal-details.md └── version-migrations │ └── 2.x-2.3.0.md ├── fastlane └── Fastfile ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── gravatar-quickeditor ├── .gitignore ├── QuickEditor.md ├── api │ └── gravatar-quickeditor.api ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro ├── screenshotTests │ └── roborazzi │ │ ├── com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorExtrasAndOtherSectionLoaded.png │ │ ├── com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorExtrasLoaded.png │ │ ├── com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoaded.png │ │ ├── com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoadedDark.png │ │ ├── com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoadedLandscape.png │ │ ├── com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoading.png │ │ ├── com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorNoInternetError.png │ │ ├── com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorPersonalLoaded.png │ │ ├── com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorSaving.png │ │ ├── com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.heightRestrictedAboutEditorLoaded.png │ │ ├── com.gravatar.quickeditor.ui.alttext.AltTextPageTest.emptyAltTextPageLoaded.png │ │ ├── com.gravatar.quickeditor.ui.alttext.AltTextPageTest.emptyAltTextPageLoadedDark.png │ │ ├── com.gravatar.quickeditor.ui.alttext.AltTextPageTest.initialAltTextPageLoaded.png │ │ ├── com.gravatar.quickeditor.ui.alttext.AltTextPageTest.initialAltTextPageLoadedDark.png │ │ ├── com.gravatar.quickeditor.ui.alttext.AltTextPageTest.updatedAltTextPageLoaded.png │ │ ├── com.gravatar.quickeditor.ui.alttext.AltTextPageTest.updatedAltTextPageLoadedDark.png │ │ ├── com.gravatar.quickeditor.ui.avatarpicker.AvatarPickerTest.avatarPickerListLoaded.png │ │ ├── com.gravatar.quickeditor.ui.avatarpicker.AvatarPickerTest.avatarPickerListLoadedDark.png │ │ ├── com.gravatar.quickeditor.ui.components.AlertBannerTest.alertBannerDark.png │ │ ├── com.gravatar.quickeditor.ui.components.AlertBannerTest.alertBannerLight.png │ │ ├── com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerButtonDisabled.png │ │ ├── com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerListEmpty.png │ │ ├── com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerListLoaded.png │ │ ├── com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerListLoadedDark.png │ │ ├── com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerVertical.png │ │ ├── com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerVerticalEmpty.png │ │ ├── com.gravatar.quickeditor.ui.components.CtaSectionTest.ctaSectionDark.png │ │ ├── com.gravatar.quickeditor.ui.components.CtaSectionTest.ctaSectionLight.png │ │ ├── com.gravatar.quickeditor.ui.components.CtaSectionTest.ctaSectionNoTitleLight.png │ │ ├── com.gravatar.quickeditor.ui.components.PopupButtonTest.popupButtonDarkMode.png │ │ ├── com.gravatar.quickeditor.ui.components.PopupButtonTest.popupButtonEllipsizedText.png │ │ ├── com.gravatar.quickeditor.ui.components.PopupButtonTest.popupButtonLightMode.png │ │ ├── com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardDarkMode.png │ │ ├── com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardLightMode.png │ │ ├── com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardWithEditButtons.png │ │ ├── com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardWithEditButtonsDarkMode.png │ │ ├── com.gravatar.quickeditor.ui.components.QESectionMessageTest.qeTextTestNoOrphan.png │ │ ├── com.gravatar.quickeditor.ui.components.QETopBarTest.qrTopBarDark.png │ │ ├── com.gravatar.quickeditor.ui.components.QETopBarTest.qrTopBarLight.png │ │ ├── com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarFailure.png │ │ ├── com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarLoading.png │ │ ├── com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarNotSelected.png │ │ ├── com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarReloadDisabled.png │ │ ├── com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarReloadEnabled.png │ │ ├── com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarSelected.png │ │ ├── com.gravatar.quickeditor.ui.components.UploadImageGridButtonTest.uploadImageGridButtonDisabled.png │ │ ├── com.gravatar.quickeditor.ui.components.UploadImageGridButtonTest.uploadImageGridButtonEnabled.png │ │ ├── com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAboutEditor.png │ │ ├── com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAboutEditorLandscape.png │ │ ├── com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAvatarPicker.png │ │ ├── com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAvatarPickerWithNavigationEnabled.png │ │ ├── com.gravatar.quickeditor.ui.oauth.OAuthPageTest.oAuthPageAuthorizing.png │ │ ├── com.gravatar.quickeditor.ui.oauth.OAuthPageTest.oAuthPageDark.png │ │ └── com.gravatar.quickeditor.ui.oauth.OAuthPageTest.oAuthPageLight.png └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── gravatar │ │ │ └── quickeditor │ │ │ ├── GravatarQuickEditor.kt │ │ │ ├── QuickEditorContainer.kt │ │ │ ├── QuickEditorFileProvider.kt │ │ │ ├── data │ │ │ ├── AcceptedLanguageInterceptor.kt │ │ │ ├── FileUtils.kt │ │ │ ├── ImageDownloader.kt │ │ │ ├── datastore │ │ │ │ └── SecureDataStoreUtils.kt │ │ │ ├── models │ │ │ │ └── QuickEditorError.kt │ │ │ ├── repository │ │ │ │ ├── AvatarRepository.kt │ │ │ │ └── ProfileRepository.kt │ │ │ └── storage │ │ │ │ ├── AvatarStorage.kt │ │ │ │ ├── DataStoreProfileStorage.kt │ │ │ │ ├── DataStoreTokenStorage.kt │ │ │ │ ├── InMemoryTokenStorage.kt │ │ │ │ ├── ProfileStorage.kt │ │ │ │ └── TokenStorage.kt │ │ │ ├── initializer │ │ │ └── QuickEditorContainerInitializer.kt │ │ │ └── ui │ │ │ ├── GravatarQuickEditorActivity.kt │ │ │ ├── PermissionUtils.kt │ │ │ ├── StringExtensions.kt │ │ │ ├── abouteditor │ │ │ ├── AboutEditor.kt │ │ │ ├── AboutEditorAction.kt │ │ │ ├── AboutEditorEvent.kt │ │ │ ├── AboutEditorField.kt │ │ │ ├── AboutEditorUiState.kt │ │ │ ├── AboutEditorViewModel.kt │ │ │ └── components │ │ │ │ ├── AboutEditField.kt │ │ │ │ ├── AboutEditSectionLabel.kt │ │ │ │ ├── AboutFieldsSection.kt │ │ │ │ ├── AboutSection.kt │ │ │ │ └── DiscardChangesAlertDialog.kt │ │ │ ├── alttext │ │ │ ├── AltTextAction.kt │ │ │ ├── AltTextEvent.kt │ │ │ ├── AltTextPage.kt │ │ │ ├── AltTextUiState.kt │ │ │ └── AltTextViewModel.kt │ │ │ ├── avatarpicker │ │ │ ├── AvatarPicker.kt │ │ │ ├── AvatarPickerAction.kt │ │ │ ├── AvatarPickerEvent.kt │ │ │ ├── AvatarPickerUiState.kt │ │ │ └── AvatarPickerViewModel.kt │ │ │ ├── components │ │ │ ├── AlertBanner.kt │ │ │ ├── AvatarDeletionConfirmationDialog.kt │ │ │ ├── AvatarMoreOptionsPickerPopup.kt │ │ │ ├── AvatarsSection.kt │ │ │ ├── CtaSection.kt │ │ │ ├── DownloadManagerDisabledAlertDialog.kt │ │ │ ├── EmailLabel.kt │ │ │ ├── FailedAvatarUploadAlertDialog.kt │ │ │ ├── HorizontalAvatarsSection.kt │ │ │ ├── LazyAvatarList.kt │ │ │ ├── ListEmptyStateBox.kt │ │ │ ├── MediaPickerPopup.kt │ │ │ ├── PermissionRationaleDialog.kt │ │ │ ├── PickerPopup.kt │ │ │ ├── PopupButton.kt │ │ │ ├── ProfileCard.kt │ │ │ ├── QEButton.kt │ │ │ ├── QEPage.kt │ │ │ ├── QESectionMessage.kt │ │ │ ├── QESectionTitle.kt │ │ │ ├── QETopBar.kt │ │ │ ├── SelectableAvatar.kt │ │ │ ├── UploadImageButton.kt │ │ │ ├── UploadImageGridButton.kt │ │ │ └── VerticalAvatarsSection.kt │ │ │ ├── cropperlauncher │ │ │ ├── CropperLauncher.kt │ │ │ └── UCropCropperLauncher.kt │ │ │ ├── editor │ │ │ ├── AboutInputField.kt │ │ │ ├── AuthenticationMethod.kt │ │ │ ├── AvatarPickerContentLayout.kt │ │ │ ├── GravatarQuickEditorDismissReason.kt │ │ │ ├── GravatarQuickEditorPage.kt │ │ │ ├── GravatarQuickEditorParams.kt │ │ │ ├── GravatarUiMode.kt │ │ │ ├── QuickEditor.kt │ │ │ ├── QuickEditorAction.kt │ │ │ ├── QuickEditorEvent.kt │ │ │ ├── QuickEditorPage.kt │ │ │ ├── QuickEditorScopeConfiguration.kt │ │ │ ├── QuickEditorScopeOption.kt │ │ │ ├── QuickEditorUiState.kt │ │ │ ├── QuickEditorViewModel.kt │ │ │ ├── UpdateHandler.kt │ │ │ ├── bottomsheet │ │ │ │ └── GravatarQuickEditorBottomSheet.kt │ │ │ └── extensions │ │ │ │ └── QuickEditorExtensions.kt │ │ │ ├── extensions │ │ │ └── QESnackbar.kt │ │ │ ├── navigation │ │ │ ├── EditorNavDestinations.kt │ │ │ └── QuickEditorPage.kt │ │ │ ├── oauth │ │ │ ├── GravatarOAuthActivity.kt │ │ │ ├── OAuthPage.kt │ │ │ ├── OAuthParams.kt │ │ │ ├── OAuthUiState.kt │ │ │ ├── OAuthViewModel.kt │ │ │ └── WordPressOauth.kt │ │ │ ├── splash │ │ │ ├── SplashAction.kt │ │ │ ├── SplashPage.kt │ │ │ └── SplashViewModel.kt │ │ │ └── time │ │ │ └── Clock.kt │ └── res │ │ ├── drawable │ │ ├── gravatar_alt_text_help.xml │ │ ├── gravatar_avatar_more_options_alt_text.xml │ │ ├── gravatar_avatar_more_options_delete.xml │ │ ├── gravatar_avatar_more_options_dots.xml │ │ ├── gravatar_avatar_more_options_download.xml │ │ ├── gravatar_avatar_more_options_rating.xml │ │ ├── gravatar_capture_photo.xml │ │ ├── gravatar_face_image.xml │ │ ├── gravatar_photo_library.xml │ │ ├── ic_checkmark.xml │ │ └── ic_edit.xml │ │ ├── values-ar │ │ └── strings.xml │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-in │ │ └── strings.xml │ │ ├── values-it │ │ └── strings.xml │ │ ├── values-iw │ │ └── strings.xml │ │ ├── values-ja │ │ └── strings.xml │ │ ├── values-ko │ │ └── strings.xml │ │ ├── values-nl │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-sv │ │ └── strings.xml │ │ ├── values-tr │ │ └── strings.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values-zh-rTW │ │ └── strings.xml │ │ ├── values │ │ ├── available_languages.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ ├── data_extraction_rules.xml │ │ └── quickeditor_filepaths.xml │ └── test │ └── java │ └── com │ └── gravatar │ └── quickeditor │ ├── AvatarTestUtils.kt │ ├── data │ ├── AcceptedLanguageInterceptorTest.kt │ ├── repository │ │ ├── AvatarRepositoryTest.kt │ │ └── ProfileRepositoryTest.kt │ └── storage │ │ ├── AvatarStorageTest.kt │ │ ├── DataStoreProfileStorageTest.kt │ │ └── DataStoreTokenStorageTest.kt │ └── ui │ ├── CoroutineTestRule.kt │ ├── GravatarScreenshotTest.kt │ ├── StringExtensionsTest.kt │ ├── abouteditor │ ├── AboutEditorTest.kt │ └── AboutEditorViewModelTest.kt │ ├── alttext │ ├── AltTextPageTest.kt │ └── AltTextViewModelTest.kt │ ├── avatarpicker │ ├── AvatarPickerTest.kt │ └── AvatarPickerViewModelTest.kt │ ├── components │ ├── AlertBannerTest.kt │ ├── AvatarsSectionTest.kt │ ├── CtaSectionTest.kt │ ├── PopupButtonTest.kt │ ├── ProfileCardTest.kt │ ├── QESectionMessageTest.kt │ ├── QETopBarTest.kt │ ├── SelectableAvatarTest.kt │ └── UploadImageGridButtonTest.kt │ ├── editor │ ├── QuickEditorScopeOptionTest.kt │ ├── QuickEditorTest.kt │ └── QuickEditorViewModelTest.kt │ ├── oauth │ ├── OAuthPageTest.kt │ └── OAuthViewModelTest.kt │ └── splash │ └── SplashViewModelTest.kt ├── gravatar-ui ├── .gitignore ├── GravatarUi.md ├── api │ └── gravatar-ui.api ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro ├── screenshotTests │ └── roborazzi │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.legacyProfileLight.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.legacyProfileLoadingLight.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileDark.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileLight.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileLoadingDark.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileLoadingLight.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileWithCustomComponents.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileTest.legacyProfileLight.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileTest.legacyProfileLoadingLight.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileTest.legacyProfileWithoutDescription.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileTest.profileDark.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileTest.profileLight.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileTest.profileLoadingDark.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileTest.profileLoadingLight.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileTest.profileWithCustomComponents.png │ │ ├── com.gravatar.gravatar.ui.components.LargeProfileTest.profileWithoutDescription.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileSummaryTest.legacyProfileLight.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileSummaryTest.legacyProfileLoadingLight.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileDark.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileEmptyStateDescription.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileLight.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileLoadingDark.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileLoadingLight.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileWithCustomComponents.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileWithoutLocation.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileTest.legacyProfileLight.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileTest.legacyProfileLoadingLight.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileTest.profileDark.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileTest.profileLight.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileTest.profileLoadingDark.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileTest.profileLoadingLight.png │ │ ├── com.gravatar.gravatar.ui.components.ProfileTest.profileWithCustomComponents.png │ │ └── com.gravatar.gravatar.ui.components.ProfileTest.profileWithEmptyDescription.png └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── gravatar │ │ │ └── ui │ │ │ ├── GravatarTheme.kt │ │ │ ├── SkeletonEffect.kt │ │ │ ├── components │ │ │ ├── ComponentState.kt │ │ │ ├── LargeProfile.kt │ │ │ ├── LargeProfileSummary.kt │ │ │ ├── Profile.kt │ │ │ ├── ProfileComponentsUtils.kt │ │ │ ├── ProfileSummary.kt │ │ │ ├── UiUtils.kt │ │ │ └── atomic │ │ │ │ ├── AboutMe.kt │ │ │ │ ├── Avatar.kt │ │ │ │ ├── DisplayName.kt │ │ │ │ ├── Location.kt │ │ │ │ ├── SocialMedia.kt │ │ │ │ ├── UserInfo.kt │ │ │ │ └── ViewProfileButton.kt │ │ │ └── theme │ │ │ ├── ColorScheme.kt │ │ │ └── ColorsPalette.kt │ └── res │ │ ├── drawable │ │ ├── gravatar_calendly_icon.xml │ │ ├── gravatar_empty_profile_avatar.xml │ │ ├── gravatar_empty_profile_avatar_dark.xml │ │ ├── gravatar_fediverse_icon.xml │ │ ├── gravatar_foursquare_icon.xml │ │ ├── gravatar_github_icon.xml │ │ ├── gravatar_gravatar_icon.xml │ │ ├── gravatar_instagram_icon.xml │ │ ├── gravatar_mastodongeneric_icon.xml │ │ ├── gravatar_stackoverflow_icon.xml │ │ ├── gravatar_tiktok_icon.xml │ │ ├── gravatar_tripit_icon.xml │ │ ├── gravatar_tumblr_icon.xml │ │ ├── gravatar_twitch_icon.xml │ │ ├── gravatar_twitter_icon.xml │ │ ├── gravatar_vimeo_icon.xml │ │ └── gravatar_wordpress_icon.xml │ │ ├── values-ar │ │ └── strings.xml │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-in │ │ └── strings.xml │ │ ├── values-it │ │ └── strings.xml │ │ ├── values-iw │ │ └── strings.xml │ │ ├── values-ja │ │ └── strings.xml │ │ ├── values-ko │ │ └── strings.xml │ │ ├── values-nl │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-sv │ │ └── strings.xml │ │ ├── values-tr │ │ └── strings.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values-zh-rTW │ │ └── strings.xml │ │ └── values │ │ ├── available_languages.xml │ │ ├── strings.xml │ │ └── values.xml │ └── test │ └── java │ └── com │ └── gravatar │ └── gravatar │ └── ui │ ├── GravatarScreenshotTest.kt │ ├── ProfileTestUtils.kt │ └── components │ ├── LargeProfileSummaryTest.kt │ ├── LargeProfileTest.kt │ ├── ProfileSummaryTest.kt │ └── ProfileTest.kt ├── gravatar ├── .gitignore ├── .idea │ ├── .gitignore │ ├── checkstyle-idea.xml │ ├── gradle.xml │ ├── migrations.xml │ └── misc.xml ├── GravatarCore.md ├── api │ └── gravatar.api ├── build.gradle.kts ├── consumer-rules.pro ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── openapi │ ├── api-gravatar.json │ └── templates │ │ ├── data_class.mustache │ │ ├── data_class_opt_var.mustache │ │ ├── data_class_req_var.mustache │ │ ├── enum_class.mustache │ │ ├── infrastructure │ │ ├── ApiAbstractions.kt.mustache │ │ ├── PartConfig.kt.mustache │ │ ├── RequestConfig.kt.mustache │ │ └── RequestMethod.kt.mustache │ │ ├── jvm-common │ │ └── infrastructure │ │ │ ├── Serializer.kt.mustache │ │ │ └── URIAdapter.kt.mustache │ │ ├── libraries │ │ └── jvm-okhttp │ │ │ ├── api.mustache │ │ │ └── infrastructure │ │ │ ├── ApiClient.kt.mustache │ │ │ └── ApiResponse.kt.mustache │ │ └── licenseInfo.mustache ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── gravatar │ │ ├── AvatarQueryOptions.kt │ │ ├── AvatarUrl.kt │ │ ├── DefaultAvatarOption.kt │ │ ├── Gravatar.kt │ │ ├── GravatarConstants.kt │ │ ├── HttpResponseCode.kt │ │ ├── ImageRating.kt │ │ ├── ProfileUrl.kt │ │ ├── di │ │ └── container │ │ │ └── GravatarSdkContainer.kt │ │ ├── extensions │ │ └── UserProfileExtensions.kt │ │ ├── logger │ │ └── Logger.kt │ │ ├── moshiadapers │ │ └── URIJsonAdapter.kt │ │ ├── restapi │ │ ├── apis │ │ │ ├── AvatarsApi.kt │ │ │ └── ProfilesApi.kt │ │ ├── infrastructure │ │ │ ├── ApiAbstractions.kt │ │ │ ├── ApiClient.kt │ │ │ ├── ApiResponse.kt │ │ │ ├── PartConfig.kt │ │ │ ├── RequestConfig.kt │ │ │ ├── RequestMethod.kt │ │ │ ├── Serializer.kt │ │ │ └── URIAdapter.kt │ │ └── models │ │ │ ├── AssociatedResponse.kt │ │ │ ├── Avatar.kt │ │ │ ├── CryptoWalletAddress.kt │ │ │ ├── Error.kt │ │ │ ├── GalleryImage.kt │ │ │ ├── Interest.kt │ │ │ ├── Language.kt │ │ │ ├── Link.kt │ │ │ ├── Profile.kt │ │ │ ├── ProfileContactInfo.kt │ │ │ ├── ProfilePayments.kt │ │ │ ├── Rating.kt │ │ │ ├── SetEmailAvatarRequest.kt │ │ │ ├── UpdateAvatarRequest.kt │ │ │ ├── UpdateProfileRequest.kt │ │ │ └── VerifiedAccount.kt │ │ ├── services │ │ ├── AvatarService.kt │ │ ├── ErrorType.kt │ │ ├── GravatarException.kt │ │ ├── GravatarListener.kt │ │ ├── GravatarResult.kt │ │ ├── HttpException.kt │ │ ├── ProfileService.kt │ │ ├── ServiceUtils.kt │ │ └── interceptors │ │ │ ├── AuthenticationInterceptor.kt │ │ │ ├── AvatarUploadTimeoutInterceptor.kt │ │ │ └── SdkVersionInterceptor.kt │ │ └── types │ │ ├── Email.kt │ │ └── Hash.kt │ └── test │ └── java │ └── com │ └── gravatar │ ├── AvatarUrlTest.kt │ ├── GravatarSdkContainerRule.kt │ ├── GravatarTest.kt │ ├── extensions │ └── UserProfileExtensionsTest.kt │ └── services │ ├── AvatarServiceTest.kt │ ├── ErrorTypeTest.kt │ ├── ProfileServiceTests.kt │ └── ServiceTestUtils.kt ├── lint.xml ├── settings.gradle.kts └── uitestutils ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml └── java └── com └── gravatar └── uitestutils └── RoborazziTest.kt /.aiexclude: -------------------------------------------------------------------------------- 1 | # Configuration files 2 | .properties 3 | 4 | # Build files 5 | build/ 6 | -------------------------------------------------------------------------------- /.buildkite/commands/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | echo "--- 🧹 Linting" 4 | ./gradlew :demo-app:lintRelease 5 | lint_exit_code=$? 6 | 7 | upload_sarif_to_github 'demo-app/build/reports/lint-results-release.sarif' 8 | 9 | exit $lint_exit_code 10 | -------------------------------------------------------------------------------- /.buildkite/commands/prototype-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | echo "--- :rubygems: Setting up Gems" 4 | install_gems 5 | 6 | echo "--- :closed_lock_with_key: Installing Secrets" 7 | bundle exec fastlane run configure_apply 8 | 9 | echo "--- :hammer_and_wrench: Building" 10 | bundle exec fastlane build_and_upload_prototype_build 11 | -------------------------------------------------------------------------------- /.buildkite/commands/publish-to-maven.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | echo "--- 🚀 Publishing" 4 | ./gradlew publishToMavenCentral --no-configuration-cache 5 | -------------------------------------------------------------------------------- /.buildkite/shared-pipeline-vars: -------------------------------------------------------------------------------- 1 | CI_TOOLKIT_PLUGIN_VERSION="5.3.1" 2 | 3 | export CI_TOOLKIT="automattic/a8c-ci-toolkit#$CI_TOOLKIT_PLUGIN_VERSION" 4 | -------------------------------------------------------------------------------- /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_PATH: "vendor/bundle" 3 | BUNDLE_JOBS: "16" 4 | BUNDLE_SPECIFIC_PLATFORM: "false" 5 | -------------------------------------------------------------------------------- /.configure: -------------------------------------------------------------------------------- 1 | { 2 | "project_name": "Gravatar-SDK-Android", 3 | "branch": "trunk", 4 | "pinned_hash": "8403414c0ed866e500fa1bc0f50113466b67da49", 5 | "files_to_copy": [ 6 | { 7 | "file": "android/Gravatar-SDK-Android/secrets.properties", 8 | "destination": "secrets.properties", 9 | "encrypt": true 10 | } 11 | ], 12 | "file_dependencies": [ 13 | 14 | ] 15 | } -------------------------------------------------------------------------------- /.configure-files/secrets.properties.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/.configure-files/secrets.properties.enc -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorConfig.org 2 | root = true 3 | 4 | [*.{kt,kts}] 5 | ktlint_code_style = ktlint_official 6 | ktlint_function_naming_ignore_when_annotated_with = Composable 7 | max_line_length=120 8 | ktlint_function_signature_body_expression_wrapping = default 9 | ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 6 10 | ktlint_standard_multiline-expression-wrapping = disabled 11 | ktlint_standard_string-template-indent = disabled 12 | 13 | [**/restapi/models/**.kt] 14 | max_line_length = off -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41E Bug report" 3 | about: Report a bug if something isn't working as expected in Gravatar Android SDK. 4 | title: '' 5 | labels: 6 | - 'Bug' 7 | - '[Priority] Medium' 8 | assignees: '' 9 | 10 | --- 11 | 12 | **Describe the bug** 13 | 14 | 15 | **To Reproduce** 16 | Steps to reproduce the behavior: 17 | 1. 18 | 19 | **Screenshots** 20 | 21 | 22 | **Expected behavior** 23 | 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "✨ New Enhancement" 3 | about: Add an idea to improve an existing feature. 4 | title: '' 5 | labels: 'Enhance' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please add details of the enhancement. 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Closes # 2 | 3 | ### Description 4 | 5 | ### Testing Steps 6 | -------------------------------------------------------------------------------- /.github/workflows/run-danger.yml: -------------------------------------------------------------------------------- 1 | name: ☢️ Trigger Danger On Buildkite 2 | 3 | on: 4 | pull_request: 5 | types: [labeled, unlabeled, milestoned, demilestoned, ready_for_review] 6 | 7 | jobs: 8 | dangermattic: 9 | if: ${{ (github.event.pull_request.draft == false) }} 10 | uses: Automattic/dangermattic/.github/workflows/reusable-retry-buildkite-step-on-events.yml@v1.1.2 11 | with: 12 | org-slug: "automattic" 13 | pipeline-slug: "gravatar-sdk-android" 14 | retry-step-key: "danger" 15 | build-commit-sha: "${{ github.event.pull_request.head.sha }}" 16 | secrets: 17 | buildkite-api-token: ${{ secrets.TRIGGER_BK_BUILD_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/submit-gradle-dependencies.yml: -------------------------------------------------------------------------------- 1 | name: Submit dependencies to GitHub Dependency Graph 2 | on: 3 | push: 4 | branches: 5 | - trunk 6 | - release/* 7 | permissions: 8 | contents: write 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | - uses: actions/setup-java@v3 17 | with: 18 | distribution: 'temurin' 19 | java-version: '17' 20 | - name: Setup Gradle to generate and submit dependency graphs 21 | uses: gradle/actions/dependency-submission@v3 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | 3 | # Intellij project files 4 | *.iml 5 | *.ipr 6 | *.iws 7 | .idea/ 8 | 9 | # Fastlane 10 | fastlane/README.md 11 | fastlane/report.xml 12 | fastlane/.env 13 | 14 | # Gems 15 | /vendor 16 | 17 | .DS_Store 18 | /build 19 | /captures 20 | .externalNativeBuild 21 | .cxx 22 | local.properties 23 | .kotlin/ 24 | 25 | secrets.properties 26 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | gravatar -------------------------------------------------------------------------------- /.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10.12.5 5 | JavaOnly 6 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.java-version: -------------------------------------------------------------------------------- 1 | 17.0 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - vendor/**/* 4 | NewCops: enable 5 | 6 | Metrics/BlockLength: 7 | Exclude: 8 | - fastlane/Fastfile 9 | 10 | Metrics/MethodLength: 11 | Max: 30 12 | 13 | Layout/LineLength: 14 | Max: 180 15 | 16 | Naming/VariableNumber: 17 | Exclude: 18 | - fastlane/Fastfile 19 | 20 | Style/HashSyntax: 21 | EnforcedShorthandSyntax: never 22 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.2.2 -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | github.dismiss_out_of_range_messages 4 | 5 | # `files: []` forces rubocop to scan all files, not just the ones modified in the PR 6 | rubocop.lint(files: [], force_exclusion: true, inline_comment: true, fail_on_inline_comment: true, 7 | include_cop_names: true) 8 | 9 | manifest_pr_checker.check_gemfile_lock_updated 10 | 11 | labels_checker.check( 12 | do_not_merge_labels: ['do not merge'], 13 | required_labels: [//], # At least one label, regardless of its name 14 | required_labels_error: 'You need to add at least one label to this PR' 15 | ) 16 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'danger-dangermattic', '~> 1.1' 6 | gem 'fastlane', '~> 2.222' 7 | gem 'fastlane-plugin-wpmreleasetoolkit', '~> 13.0' 8 | gem 'rubocop', '~> 1.65' 9 | -------------------------------------------------------------------------------- /build-logic/convention/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/com/gravatar/Detekt.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar 2 | 3 | import io.gitlab.arturbosch.detekt.extensions.DetektExtension 4 | import org.gradle.api.Project 5 | import org.gradle.kotlin.dsl.configure 6 | 7 | internal fun Project.configureDetekt() { 8 | configure { 9 | config.setFrom("${project.rootDir}/config/detekt/detekt.yml") 10 | source.setFrom("src") 11 | autoCorrect = false 12 | buildUponDefaultConfig = true 13 | parallel = true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/com/gravatar/KotlinAndroid.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar 2 | 3 | import COMPILE_SDK 4 | import MIN_SDK 5 | import com.android.build.api.dsl.CommonExtension 6 | import org.gradle.api.JavaVersion 7 | import org.gradle.api.Project 8 | import org.gradle.kotlin.dsl.configure 9 | import org.gradle.kotlin.dsl.withType 10 | import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension 11 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 12 | 13 | internal fun Project.configureKotlinAndroid(commonExtension: CommonExtension<*, *, *, *, *, *>) { 14 | commonExtension.apply { 15 | compileSdk = COMPILE_SDK 16 | 17 | defaultConfig { 18 | minSdk = MIN_SDK 19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 20 | } 21 | 22 | compileOptions { 23 | sourceCompatibility = JavaVersion.VERSION_1_8 24 | targetCompatibility = JavaVersion.VERSION_1_8 25 | } 26 | } 27 | configureKotlin() 28 | } 29 | 30 | /** 31 | * Configure base Kotlin options for JVM (non-Android) 32 | */ 33 | internal fun Project.configureKotlin() { 34 | tasks.withType().configureEach { 35 | kotlinOptions { 36 | jvmTarget = JavaVersion.VERSION_1_8.toString() 37 | } 38 | } 39 | extensions.configure { 40 | explicitApi() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/com/gravatar/ProjectExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.artifacts.VersionCatalog 5 | import org.gradle.api.artifacts.VersionCatalogsExtension 6 | import org.gradle.kotlin.dsl.getByType 7 | 8 | val Project.libs 9 | get(): VersionCatalog = extensions.getByType().named("libs") 10 | -------------------------------------------------------------------------------- /build-logic/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.parallel=true 2 | org.gradle.caching=true 3 | org.gradle.configureondemand=true 4 | -------------------------------------------------------------------------------- /build-logic/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | versionCatalogs { 8 | create("libs") { 9 | from(files("../gradle/libs.versions.toml")) 10 | } 11 | } 12 | } 13 | 14 | rootProject.name = "build-logic" 15 | include(":convention") 16 | -------------------------------------------------------------------------------- /compose_compiler_config.conf: -------------------------------------------------------------------------------- 1 | // Consider All :gravatar model&types classes stable 2 | com.gravatar.restapi.models.* 3 | com.gravatar.types.* 4 | 5 | kotlin.collections.* 6 | 7 | java.net.URL 8 | 9 | android.net.Uri 10 | 11 | com.gravatar.ui.components.ComponentState<*> 12 | -------------------------------------------------------------------------------- /demo-app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /demo-app/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 -------------------------------------------------------------------------------- /demo-app/src/main/java/com/gravatar/demoapp/DemoFileProvider.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.demoapp 2 | 3 | import androidx.core.content.FileProvider 4 | 5 | internal class DemoFileProvider : FileProvider(R.xml.filepaths) 6 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/gravatar/demoapp/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.demoapp 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.activity.enableEdgeToEdge 7 | import com.gravatar.Gravatar 8 | import com.gravatar.demoapp.ui.DemoGravatarApp 9 | 10 | class MainActivity : ComponentActivity() { 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | enableEdgeToEdge() 13 | super.onCreate(savedInstanceState) 14 | 15 | // Initialize the Gravatar SDK with the API key if it is available 16 | @Suppress("UNNECESSARY_SAFE_CALL") 17 | BuildConfig.DEMO_GRAVATAR_API_KEY?.let { Gravatar.apiKey(it).context(applicationContext) } 18 | 19 | setContent { 20 | DemoGravatarApp() 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/gravatar/demoapp/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.demoapp.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) 12 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/gravatar/demoapp/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.demoapp.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = 11 | Typography( 12 | bodyLarge = 13 | TextStyle( 14 | fontFamily = FontFamily.Default, 15 | fontWeight = FontWeight.Normal, 16 | fontSize = 16.sp, 17 | lineHeight = 24.sp, 18 | letterSpacing = 0.5.sp, 19 | ), 20 | /* Other default text styles to override 21 | titleLarge = TextStyle( 22 | fontFamily = FontFamily.Default, 23 | fontWeight = FontWeight.Normal, 24 | fontSize = 22.sp, 25 | lineHeight = 28.sp, 26 | letterSpacing = 0.sp 27 | ), 28 | labelSmall = TextStyle( 29 | fontFamily = FontFamily.Default, 30 | fontWeight = FontWeight.Medium, 31 | fontSize = 11.sp, 32 | lineHeight = 16.sp, 33 | letterSpacing = 0.5.sp 34 | ) 35 | */ 36 | ) 37 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/gravatar/demoapp/ui/components/GravatarEmailInput.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.demoapp.ui.components 2 | 3 | import androidx.compose.material3.Text 4 | import androidx.compose.material3.TextField 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.res.stringResource 8 | import com.gravatar.demoapp.R 9 | 10 | @Composable 11 | fun GravatarEmailInput(email: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier) { 12 | TextField( 13 | value = email, 14 | onValueChange = onValueChange, 15 | label = { Text(stringResource(R.string.gravatar_email_input_label)) }, 16 | maxLines = 1, 17 | modifier = modifier, 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/gravatar/demoapp/ui/model/SettingsState.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.demoapp.ui.model 2 | 3 | import com.gravatar.DefaultAvatarOption 4 | import com.gravatar.ImageRating 5 | 6 | data class SettingsState( 7 | val email: String, 8 | val size: Int?, 9 | val defaultAvatarImageEnabled: Boolean, 10 | val selectedDefaultAvatar: DefaultAvatarOption, 11 | val defaultAvatarOptions: List, 12 | val forceDefaultAvatar: Boolean, 13 | val imageRatingEnabled: Boolean, 14 | val imageRating: ImageRating, 15 | ) 16 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/gravatar/demoapp/ui/utils/UtilExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.demoapp.ui.utils 2 | 3 | @Suppress("MagicNumber") 4 | fun Any.prettyPrint() = toString().let { toString -> 5 | var indentLevel = 0 6 | val indentWidth = 4 7 | 8 | fun padding() = "".padStart(indentLevel * indentWidth) 9 | buildString { 10 | var ignoreSpace = false 11 | toString.onEach { char -> 12 | when (char) { 13 | '(', '[', '{' -> { 14 | indentLevel++ 15 | appendLine(char) 16 | append(padding()) 17 | } 18 | 19 | ')', ']', '}' -> { 20 | indentLevel-- 21 | appendLine() 22 | append(padding()) 23 | append(char) 24 | } 25 | 26 | ',' -> { 27 | appendLine(char) 28 | append(padding()) 29 | ignoreSpace = true 30 | } 31 | 32 | ' ' -> { 33 | if (!ignoreSpace) append(char) 34 | ignoreSpace = false 35 | } 36 | 37 | else -> append(char) 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-hdpi/ic_launcher_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-hdpi/ic_launcher_background.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-mdpi/ic_launcher_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-mdpi/ic_launcher_background.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/demo-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /demo-app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | -------------------------------------------------------------------------------- /demo-app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | 11 | -------------------------------------------------------------------------------- /demo-app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Gravatar Demo App 3 | Gravatar E-Mail 4 | Gravatar URL 5 | Gravatar Hash 6 | Avatar Size 7 | Default Avatar 8 | Force Default Avatar 9 | Image Rating 10 | Unknown error 11 | Profile 12 | Avatar 13 | Avatar Update 14 | Get Profile 15 | Skeleton 16 | Display Name: %1$s 17 | Url: %1$s 18 | Load Gravatar 19 | Theme 20 | Upload success 21 | Upload error: %1$s 22 | Raw Profile 23 | Open QE 24 | 25 | -------------------------------------------------------------------------------- /demo-app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | -------------------------------------------------------------------------------- /demo-app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /demo-app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 11 | 12 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /demo-app/src/main/res/xml/filepaths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /demo-app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo-app/src/test/java/com/gravatar/lib/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.lib 2 | 3 | import junit.framework.TestCase.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/get-started/screenshot-developers-portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/get-started/screenshot-developers-portal.png -------------------------------------------------------------------------------- /docs/images/about_editor_all_fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/about_editor_all_fields.png -------------------------------------------------------------------------------- /docs/images/about_editor_two_fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/about_editor_two_fields.png -------------------------------------------------------------------------------- /docs/images/avatar_and_about.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/avatar_and_about.gif -------------------------------------------------------------------------------- /docs/images/avatar_picker_horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/avatar_picker_horizontal.png -------------------------------------------------------------------------------- /docs/images/avatar_picker_vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/avatar_picker_vertical.png -------------------------------------------------------------------------------- /docs/images/dokka/logo-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/images/gravatar_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/gravatar_logo.png -------------------------------------------------------------------------------- /docs/images/large_profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/large_profile.png -------------------------------------------------------------------------------- /docs/images/large_profile_summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/large_profile_summary.png -------------------------------------------------------------------------------- /docs/images/profile_light_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/profile_light_demo.png -------------------------------------------------------------------------------- /docs/images/profile_summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/profile_summary.png -------------------------------------------------------------------------------- /docs/images/redundant_visibility_modifier_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/docs/images/redundant_visibility_modifier_warning.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gravatar-quickeditor/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /gravatar-quickeditor/QuickEditor.md: -------------------------------------------------------------------------------- 1 | # Module quickeditor 2 | This module contains the quickeditor components for the Gravatar SDK. 3 | 4 | # Package com.gravatar.quickeditor 5 | This package includes the quickeditor components provided with the Gravatar SDK. 6 | -------------------------------------------------------------------------------- /gravatar-quickeditor/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/consumer-rules.pro -------------------------------------------------------------------------------- /gravatar-quickeditor/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 -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorExtrasAndOtherSectionLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorExtrasAndOtherSectionLoaded.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorExtrasLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorExtrasLoaded.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoaded.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoadedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoadedDark.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoadedLandscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoadedLandscape.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorLoading.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorNoInternetError.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorNoInternetError.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorPersonalLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorPersonalLoaded.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorSaving.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.aboutEditorSaving.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.heightRestrictedAboutEditorLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.abouteditor.AboutEditorTest.heightRestrictedAboutEditorLoaded.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.emptyAltTextPageLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.emptyAltTextPageLoaded.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.emptyAltTextPageLoadedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.emptyAltTextPageLoadedDark.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.initialAltTextPageLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.initialAltTextPageLoaded.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.initialAltTextPageLoadedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.initialAltTextPageLoadedDark.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.updatedAltTextPageLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.updatedAltTextPageLoaded.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.updatedAltTextPageLoadedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.alttext.AltTextPageTest.updatedAltTextPageLoadedDark.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.avatarpicker.AvatarPickerTest.avatarPickerListLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.avatarpicker.AvatarPickerTest.avatarPickerListLoaded.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.avatarpicker.AvatarPickerTest.avatarPickerListLoadedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.avatarpicker.AvatarPickerTest.avatarPickerListLoadedDark.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AlertBannerTest.alertBannerDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AlertBannerTest.alertBannerDark.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AlertBannerTest.alertBannerLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AlertBannerTest.alertBannerLight.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerButtonDisabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerButtonDisabled.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerListEmpty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerListEmpty.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerListLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerListLoaded.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerListLoadedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerListLoadedDark.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerVertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerVertical.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerVerticalEmpty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.AvatarsSectionTest.avatarPickerVerticalEmpty.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.CtaSectionTest.ctaSectionDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.CtaSectionTest.ctaSectionDark.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.CtaSectionTest.ctaSectionLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.CtaSectionTest.ctaSectionLight.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.CtaSectionTest.ctaSectionNoTitleLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.CtaSectionTest.ctaSectionNoTitleLight.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.PopupButtonTest.popupButtonDarkMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.PopupButtonTest.popupButtonDarkMode.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.PopupButtonTest.popupButtonEllipsizedText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.PopupButtonTest.popupButtonEllipsizedText.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.PopupButtonTest.popupButtonLightMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.PopupButtonTest.popupButtonLightMode.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardDarkMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardDarkMode.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardLightMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardLightMode.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardWithEditButtons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardWithEditButtons.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardWithEditButtonsDarkMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.ProfileCardTest.profileCardWithEditButtonsDarkMode.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.QESectionMessageTest.qeTextTestNoOrphan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.QESectionMessageTest.qeTextTestNoOrphan.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.QETopBarTest.qrTopBarDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.QETopBarTest.qrTopBarDark.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.QETopBarTest.qrTopBarLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.QETopBarTest.qrTopBarLight.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarFailure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarFailure.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarLoading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarLoading.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarNotSelected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarNotSelected.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarReloadDisabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarReloadDisabled.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarReloadEnabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarReloadEnabled.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarSelected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.SelectableAvatarTest.selectableAvatarSelected.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.UploadImageGridButtonTest.uploadImageGridButtonDisabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.UploadImageGridButtonTest.uploadImageGridButtonDisabled.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.UploadImageGridButtonTest.uploadImageGridButtonEnabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.components.UploadImageGridButtonTest.uploadImageGridButtonEnabled.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAboutEditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAboutEditor.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAboutEditorLandscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAboutEditorLandscape.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAvatarPicker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAvatarPicker.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAvatarPickerWithNavigationEnabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.editor.QuickEditorTest.quickEditorWithAvatarPickerWithNavigationEnabled.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.oauth.OAuthPageTest.oAuthPageAuthorizing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.oauth.OAuthPageTest.oAuthPageAuthorizing.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.oauth.OAuthPageTest.oAuthPageDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.oauth.OAuthPageTest.oAuthPageDark.png -------------------------------------------------------------------------------- /gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.oauth.OAuthPageTest.oAuthPageLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-quickeditor/screenshotTests/roborazzi/com.gravatar.quickeditor.ui.oauth.OAuthPageTest.oAuthPageLight.png -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/QuickEditorFileProvider.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import androidx.core.content.FileProvider 6 | import java.io.File 7 | 8 | internal class QuickEditorFileProvider : FileProvider(R.xml.quickeditor_filepaths) { 9 | companion object { 10 | fun getTempCameraImageUri(context: Context): Uri { 11 | val directory = File(context.cacheDir, "quickeditor") 12 | directory.mkdirs() 13 | val file = File(directory, "temp_camera_image.jpg") 14 | val authority = "${context.packageName}.com.quickeditor.fileprovider" 15 | return getUriForFile( 16 | context, 17 | authority, 18 | file, 19 | ) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/data/FileUtils.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.data 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import androidx.core.net.toFile 6 | import java.io.File 7 | 8 | internal class FileUtils( 9 | private val context: Context, 10 | ) { 11 | fun createCroppedAvatarFile(): File { 12 | return File(context.cacheDir, "cropped_avatar_${System.currentTimeMillis()}.jpg") 13 | } 14 | 15 | fun deleteFile(uri: Uri) { 16 | val toFile = uri.toFile() 17 | toFile.delete() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/data/datastore/SecureDataStoreUtils.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.data.datastore 2 | 3 | import android.content.Context 4 | import androidx.core.content.edit 5 | import androidx.datastore.preferences.preferencesDataStoreFile 6 | import androidx.security.crypto.EncryptedFile 7 | import androidx.security.crypto.MasterKeys 8 | import java.io.File 9 | 10 | /** 11 | * This creates the encrypted file and fallbacks to a new file when previous one was restored from the 12 | * backup on another device. 13 | */ 14 | @Suppress("TooGenericExceptionCaught", "SwallowedException") 15 | internal fun Context.createEncryptedFileWithFallbackReset(name: String): EncryptedFile { 16 | val plainFile = preferencesDataStoreFile(name) 17 | val encryptedFile = try { 18 | encryptedFile(plainFile) 19 | } catch (e: Exception) { 20 | clearMasterKey() 21 | plainFile.delete() 22 | encryptedFile(plainFile) 23 | } 24 | return encryptedFile 25 | } 26 | 27 | private fun Context.encryptedFile(file: File) = EncryptedFile.Builder( 28 | file, 29 | this, 30 | MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), 31 | EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB, 32 | ).build() 33 | 34 | private fun Context.clearMasterKey() { 35 | // the name comes from private EncryptedFile.KEYSET_PREF_NAME 36 | getSharedPreferences( 37 | "__androidx_security_crypto_encrypted_file_pref__", 38 | Context.MODE_PRIVATE, 39 | ).edit { 40 | clear() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/data/models/QuickEditorError.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.data.models 2 | 3 | import com.gravatar.services.ErrorType 4 | 5 | internal sealed class QuickEditorError { 6 | data object TokenNotFound : QuickEditorError() 7 | 8 | data object Unknown : QuickEditorError() 9 | 10 | data class Request( 11 | val type: ErrorType, 12 | ) : QuickEditorError() 13 | } 14 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/data/storage/DataStoreProfileStorage.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.data.storage 2 | 3 | import androidx.datastore.core.DataStore 4 | import androidx.datastore.preferences.core.Preferences 5 | import androidx.datastore.preferences.core.booleanPreferencesKey 6 | import androidx.datastore.preferences.core.edit 7 | import kotlinx.coroutines.CoroutineDispatcher 8 | import kotlinx.coroutines.flow.first 9 | import kotlinx.coroutines.withContext 10 | import java.io.IOException 11 | 12 | internal class DataStoreProfileStorage( 13 | private val dataStore: DataStore, 14 | private val dispatcher: CoroutineDispatcher, 15 | ) : ProfileStorage { 16 | private val loginIntroShownKeyPrefix = "login_intro_shown" 17 | 18 | override suspend fun setLoginIntroShown(emailHash: String): Unit = withContext(dispatcher) { 19 | dataStore.edit { preferences -> 20 | preferences[booleanPreferencesKey(emailHash.loginIntroShownKey)] = true 21 | } 22 | } 23 | 24 | @Suppress("SwallowedException") 25 | override suspend fun getLoginIntroShown(emailHash: String): Boolean = withContext(dispatcher) { 26 | try { 27 | dataStore.data.first()[booleanPreferencesKey(emailHash.loginIntroShownKey)] ?: false 28 | } catch (exception: IOException) { 29 | false 30 | } 31 | } 32 | 33 | private val String.loginIntroShownKey: String 34 | get() = "${loginIntroShownKeyPrefix}_$this" 35 | } 36 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/data/storage/DataStoreTokenStorage.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.data.storage 2 | 3 | import androidx.datastore.core.DataStore 4 | import androidx.datastore.core.IOException 5 | import androidx.datastore.preferences.core.Preferences 6 | import androidx.datastore.preferences.core.edit 7 | import androidx.datastore.preferences.core.stringPreferencesKey 8 | import kotlinx.coroutines.CoroutineDispatcher 9 | import kotlinx.coroutines.flow.first 10 | import kotlinx.coroutines.withContext 11 | 12 | internal class DataStoreTokenStorage( 13 | private val dataStore: DataStore, 14 | private val dispatcher: CoroutineDispatcher, 15 | ) : TokenStorage { 16 | override suspend fun storeToken(key: String, token: String): Unit = withContext(dispatcher) { 17 | dataStore.edit { preferences -> 18 | preferences[stringPreferencesKey(key)] = token 19 | } 20 | } 21 | 22 | @Suppress("SwallowedException") 23 | override suspend fun getToken(key: String): String? = withContext(dispatcher) { 24 | try { 25 | dataStore.data.first()[stringPreferencesKey(key)] 26 | } catch (exception: IOException) { 27 | null 28 | } 29 | } 30 | 31 | override suspend fun deleteToken(key: String): Unit = withContext(dispatcher) { 32 | dataStore.edit { preferences -> 33 | preferences.remove(stringPreferencesKey(key)) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/data/storage/InMemoryTokenStorage.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.data.storage 2 | 3 | import java.util.concurrent.ConcurrentHashMap 4 | 5 | internal class InMemoryTokenStorage : TokenStorage { 6 | private val tokens = ConcurrentHashMap() 7 | 8 | override suspend fun storeToken(key: String, token: String) { 9 | tokens.put(key, token) 10 | } 11 | 12 | override suspend fun getToken(key: String): String? { 13 | return tokens.get(key) 14 | } 15 | 16 | override suspend fun deleteToken(key: String) { 17 | tokens.remove(key) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/data/storage/ProfileStorage.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.data.storage 2 | 3 | internal interface ProfileStorage { 4 | suspend fun setLoginIntroShown(emailHash: String) 5 | 6 | suspend fun getLoginIntroShown(emailHash: String): Boolean 7 | } 8 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/data/storage/TokenStorage.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.data.storage 2 | 3 | internal interface TokenStorage { 4 | suspend fun storeToken(key: String, token: String) 5 | 6 | suspend fun getToken(key: String): String? 7 | 8 | suspend fun deleteToken(key: String) 9 | } 10 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/initializer/QuickEditorContainerInitializer.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.initializer 2 | 3 | import android.content.Context 4 | import androidx.startup.Initializer 5 | import com.gravatar.quickeditor.QuickEditorContainer 6 | 7 | internal class QuickEditorContainerInitializer : Initializer { 8 | override fun create(context: Context): QuickEditorContainer { 9 | return QuickEditorContainer.init(context) 10 | } 11 | 12 | override fun dependencies(): List>> { 13 | return emptyList() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/StringExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui 2 | 3 | internal val String.removeOrphans: String 4 | get() { 5 | val space = " " 6 | val index = lastIndexOf(space, ignoreCase = true) 7 | return if (index < 0) this else this.replaceRange(index, index + space.length, "\u00A0") 8 | } 9 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/abouteditor/AboutEditorAction.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.abouteditor 2 | 3 | import com.gravatar.restapi.models.Profile 4 | 5 | internal sealed class AboutEditorAction { 6 | data class ProfileUpdated(val profile: Profile) : AboutEditorAction() 7 | 8 | data object ProfileUpdateFailed : AboutEditorAction() 9 | 10 | data object InvokeAuthFailed : AboutEditorAction() 11 | } 12 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/abouteditor/AboutEditorEvent.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.abouteditor 2 | 3 | internal sealed class AboutEditorEvent { 4 | data class OnAboutFieldUpdated( 5 | val aboutField: AboutEditorField, 6 | ) : AboutEditorEvent() 7 | 8 | data object OnSaveClicked : AboutEditorEvent() 9 | 10 | data object HandleAuthFailureTapped : AboutEditorEvent() 11 | 12 | data object Refresh : AboutEditorEvent() 13 | 14 | data class OnCompactWindowEnabled(val enabled: Boolean) : AboutEditorEvent() 15 | } 16 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/abouteditor/AboutEditorField.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.abouteditor 2 | 3 | import com.gravatar.quickeditor.ui.editor.AboutInputField 4 | 5 | internal data class AboutEditorField( 6 | val type: AboutInputField, 7 | val value: String, 8 | val maxLines: Int = 1, 9 | ) 10 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/abouteditor/AboutEditorUiState.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.abouteditor 2 | 3 | import com.gravatar.quickeditor.ui.avatarpicker.SectionError 4 | 5 | internal data class AboutEditorUiState( 6 | val aboutFields: Set = emptySet(), 7 | val isLoading: Boolean = false, 8 | val savingProfile: Boolean = false, 9 | val discardChangesDialogVisible: Boolean = false, 10 | val error: SectionError? = null, 11 | val savedAboutFields: Set = emptySet(), 12 | val compactWindow: Boolean = false, 13 | ) { 14 | val formEnabled: Boolean = !savingProfile 15 | 16 | val unsavedChanges: Boolean = aboutFields != savedAboutFields 17 | 18 | val saveEnabled: Boolean = !isLoading && unsavedChanges 19 | } 20 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/abouteditor/components/AboutEditSectionLabel.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.abouteditor.components 2 | 3 | import androidx.compose.material3.MaterialTheme 4 | import androidx.compose.material3.Text 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | 8 | @Composable 9 | internal fun AboutEditSectionLabel(title: String, modifier: Modifier = Modifier) { 10 | Text( 11 | text = title, 12 | style = MaterialTheme.typography.titleSmall, 13 | modifier = modifier, 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/alttext/AltTextAction.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.alttext 2 | 3 | internal sealed class AltTextAction { 4 | data object AltTextUpdated : AltTextAction() 5 | 6 | data object AltTextUpdateFailed : AltTextAction() 7 | 8 | data object AvatarCantBeLoaded : AltTextAction() 9 | } 10 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/alttext/AltTextEvent.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.alttext 2 | 3 | internal sealed class AltTextEvent { 4 | data class AvatarAltTextChange(val newAltText: String) : AltTextEvent() 5 | 6 | data object AvatarAltTextSaveTapped : AltTextEvent() 7 | } 8 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/alttext/AltTextUiState.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.alttext 2 | 3 | import java.net.URI 4 | 5 | internal data class AltTextUiState( 6 | val avatarUrl: URI? = null, 7 | val isUpdating: Boolean, 8 | val altText: String = "", 9 | val altTextMaxLength: Int, 10 | private val initialAltText: String = altText, 11 | ) { 12 | val isSaveButtonEnabled: Boolean 13 | get() = altText != initialAltText && !isUpdating 14 | } 15 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/avatarpicker/AvatarPickerAction.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.avatarpicker 2 | 3 | import android.net.Uri 4 | import com.gravatar.restapi.models.Avatar 5 | import com.gravatar.types.Email 6 | import java.io.File 7 | 8 | internal sealed class AvatarPickerAction { 9 | data object AvatarSelected : AvatarPickerAction() 10 | 11 | data class LaunchImageCropper(val imageUri: Uri, val tempFile: File) : AvatarPickerAction() 12 | 13 | data object AvatarSelectionFailed : AvatarPickerAction() 14 | 15 | data object InvokeAuthFailed : AvatarPickerAction() 16 | 17 | data object AvatarDownloadStarted : AvatarPickerAction() 18 | 19 | data object DownloadManagerNotAvailable : AvatarPickerAction() 20 | 21 | data class AvatarDeletionFailed(val avatarId: String) : AvatarPickerAction() 22 | 23 | data object AvatarRatingUpdated : AvatarPickerAction() 24 | 25 | data object AvatarRatingUpdateFailed : AvatarPickerAction() 26 | 27 | data class LaunchAvatarAltText(val email: Email, val avatar: Avatar) : AvatarPickerAction() 28 | } 29 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/avatarpicker/AvatarPickerEvent.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.avatarpicker 2 | 3 | import android.net.Uri 4 | import com.gravatar.restapi.models.Avatar 5 | 6 | internal sealed class AvatarPickerEvent { 7 | data object Refresh : AvatarPickerEvent() 8 | 9 | data class AvatarSelected(val avatar: Avatar) : AvatarPickerEvent() 10 | 11 | data class LocalImageSelected(val uri: Uri) : AvatarPickerEvent() 12 | 13 | data class ImageCropped(val uri: Uri) : AvatarPickerEvent() 14 | 15 | data class FailedAvatarTapped(val uri: Uri) : AvatarPickerEvent() 16 | 17 | data class FailedAvatarDismissed(val uri: Uri) : AvatarPickerEvent() 18 | 19 | data object FailedAvatarDialogDismissed : AvatarPickerEvent() 20 | 21 | data object HandleAuthFailureTapped : AvatarPickerEvent() 22 | 23 | data class DownloadAvatarTapped(val avatar: Avatar) : AvatarPickerEvent() 24 | 25 | data object DownloadManagerDisabledDialogDismissed : AvatarPickerEvent() 26 | 27 | data class AvatarDeleteSelected(val avatarId: String) : AvatarPickerEvent() 28 | 29 | data object AvatarDeleteAlertDismissed : AvatarPickerEvent() 30 | 31 | data class AvatarRatingSelected(val avatarId: String, val rating: Avatar.Rating) : AvatarPickerEvent() 32 | 33 | data class AvatarAltTextTapped(val avatarId: String) : AvatarPickerEvent() 34 | } 35 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/components/AvatarDeletionConfirmationDialog.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.compose.material3.AlertDialog 4 | import androidx.compose.material3.MaterialTheme 5 | import androidx.compose.material3.Text 6 | import androidx.compose.material3.TextButton 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.res.stringResource 9 | import com.gravatar.quickeditor.R 10 | 11 | @Composable 12 | internal fun AvatarDeletionConfirmationDialog(onConfirm: () -> Unit, onDismiss: () -> Unit) { 13 | AlertDialog( 14 | onDismissRequest = onDismiss, 15 | title = { 16 | Text(text = stringResource(R.string.gravatar_qe_avatar_delete_confirmation_title)) 17 | }, 18 | text = { 19 | Text(text = stringResource(R.string.gravatar_qe_avatar_delete_confirmation_message)) 20 | }, 21 | confirmButton = { 22 | TextButton(onClick = onConfirm) { 23 | Text( 24 | text = stringResource(R.string.gravatar_qe_avatar_delete_confirmation_confirm), 25 | color = MaterialTheme.colorScheme.error, 26 | ) 27 | } 28 | }, 29 | dismissButton = { 30 | TextButton(onClick = onDismiss) { 31 | Text(text = stringResource(R.string.gravatar_qe_avatar_delete_confirmation_cancel)) 32 | } 33 | }, 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/components/DownloadManagerDisabledAlertDialog.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.compose.material3.AlertDialog 4 | import androidx.compose.material3.Text 5 | import androidx.compose.material3.TextButton 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.res.stringResource 8 | import com.gravatar.quickeditor.R 9 | 10 | @Composable 11 | internal fun DownloadManagerDisabledAlertDialog(isVisible: Boolean, onDismiss: () -> Unit, onConfirm: () -> Unit) { 12 | if (isVisible) { 13 | AlertDialog( 14 | onDismissRequest = onDismiss, 15 | text = { Text(text = stringResource(id = R.string.gravatar_qe_download_manager_disabled_title)) }, 16 | confirmButton = { 17 | TextButton( 18 | onClick = onConfirm, 19 | ) { 20 | Text(text = stringResource(id = R.string.gravatar_qe_permission_rationale_open_settings)) 21 | } 22 | }, 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/components/EmailLabel.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.compose.material3.MaterialTheme 4 | import androidx.compose.material3.Text 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.text.style.TextAlign 8 | import androidx.compose.ui.tooling.preview.Preview 9 | import androidx.compose.ui.unit.sp 10 | import com.gravatar.types.Email 11 | import com.gravatar.ui.GravatarTheme 12 | 13 | @Composable 14 | internal fun EmailLabel(email: Email, modifier: Modifier = Modifier) { 15 | Text( 16 | text = email.toString(), 17 | color = MaterialTheme.colorScheme.onSurfaceVariant, 18 | textAlign = TextAlign.Center, 19 | fontSize = 13.sp, 20 | maxLines = 1, 21 | modifier = modifier, 22 | ) 23 | } 24 | 25 | @Preview(showBackground = true) 26 | @Composable 27 | private fun EmailLabelPreview() { 28 | GravatarTheme { 29 | EmailLabel(email = Email("william.henry.harrison@example.com")) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/components/ListEmptyStateBox.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Alignment 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.res.painterResource 11 | import androidx.compose.ui.res.stringResource 12 | import androidx.compose.ui.unit.dp 13 | import com.gravatar.quickeditor.R 14 | 15 | @Composable 16 | internal fun ListEmptyStateBox(modifier: Modifier = Modifier) { 17 | Box(modifier = modifier.fillMaxWidth()) { 18 | Image( 19 | modifier = Modifier 20 | .align(Alignment.Center) 21 | .padding(top = 24.dp), 22 | painter = painterResource(id = R.drawable.gravatar_face_image), 23 | contentDescription = stringResource(R.string.gravatar_qe_happy_face_image_content_description), 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/components/QEPage.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.activity.compose.BackHandler 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | 8 | @Composable 9 | internal fun QEPageDefault(onDoneClicked: () -> Unit, content: @Composable () -> Unit, modifier: Modifier = Modifier) { 10 | BackHandler { 11 | onDoneClicked() 12 | } 13 | 14 | QEPage( 15 | topBar = { 16 | QETopBar({ 17 | QETopBarTextButton( 18 | onClick = onDoneClicked, 19 | ) 20 | }) 21 | }, 22 | content = content, 23 | modifier = modifier, 24 | ) 25 | } 26 | 27 | @Composable 28 | internal fun QEPage(topBar: @Composable () -> Unit, content: @Composable () -> Unit, modifier: Modifier = Modifier) { 29 | Column(modifier) { 30 | topBar() 31 | content() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/components/QESectionMessage.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.compose.material3.MaterialTheme 4 | import androidx.compose.material3.Text 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.res.stringResource 8 | import androidx.compose.ui.tooling.preview.Preview 9 | import androidx.compose.ui.unit.sp 10 | import com.gravatar.quickeditor.R 11 | import com.gravatar.quickeditor.ui.removeOrphans 12 | import com.gravatar.ui.GravatarTheme 13 | 14 | @Composable 15 | internal fun QESectionMessage(message: String, modifier: Modifier = Modifier) { 16 | Text( 17 | text = message.removeOrphans, 18 | fontSize = 15.sp, 19 | color = MaterialTheme.colorScheme.tertiary, 20 | modifier = modifier, 21 | ) 22 | } 23 | 24 | @Composable 25 | @Preview(showBackground = true, widthDp = 300) 26 | private fun QESectionMessagePreview() { 27 | GravatarTheme { 28 | QESectionMessage(message = stringResource(id = R.string.gravatar_qe_avatar_picker_description)) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/components/QESectionTitle.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.compose.material3.Text 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.ui.Modifier 6 | import androidx.compose.ui.res.stringResource 7 | import androidx.compose.ui.text.font.FontWeight 8 | import androidx.compose.ui.tooling.preview.Preview 9 | import androidx.compose.ui.unit.sp 10 | import com.gravatar.quickeditor.R 11 | import com.gravatar.ui.GravatarTheme 12 | 13 | @Composable 14 | internal fun QESectionTitle(title: String, modifier: Modifier = Modifier) { 15 | Text( 16 | text = title, 17 | fontSize = 22.sp, 18 | fontWeight = FontWeight.Bold, 19 | modifier = modifier, 20 | ) 21 | } 22 | 23 | @Composable 24 | @Preview(showBackground = true) 25 | private fun QESectionTitlePreview() { 26 | GravatarTheme { 27 | QESectionTitle(title = stringResource(id = R.string.gravatar_qe_avatar_picker_title)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/cropperlauncher/CropperLauncher.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.cropperlauncher 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.net.Uri 6 | import androidx.activity.result.ActivityResultLauncher 7 | import java.io.File 8 | 9 | internal interface CropperLauncher { 10 | fun launch(launcher: ActivityResultLauncher, image: Uri, tempFile: File, context: Context) 11 | } 12 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/editor/AuthenticationMethod.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.editor 2 | 3 | import android.os.Parcelable 4 | import com.gravatar.quickeditor.ui.oauth.OAuthParams 5 | import kotlinx.parcelize.Parcelize 6 | import java.util.Objects 7 | 8 | /** 9 | * Represents the authentication method used for the Gravatar Quick Editor. 10 | */ 11 | @Parcelize 12 | public sealed class AuthenticationMethod : Parcelable { 13 | /** 14 | * OAuth authentication method. 15 | * 16 | * @property oAuthParams The OAuth parameters. 17 | */ 18 | public class OAuth(public val oAuthParams: OAuthParams) : AuthenticationMethod() { 19 | override fun hashCode(): Int = Objects.hash(oAuthParams) 20 | 21 | override fun equals(other: Any?): Boolean = other is OAuth && other.oAuthParams == oAuthParams 22 | 23 | override fun toString(): String = "Authentication.OAuth(oAuthParams=$oAuthParams)" 24 | } 25 | 26 | /** 27 | * Bearer authentication method. If the token is invalid or expired, the user will be 28 | * presented with the error state and an option to close the Quick Editor. 29 | * 30 | * @property token The bearer token. 31 | */ 32 | public class Bearer(public val token: String) : AuthenticationMethod() { 33 | override fun hashCode(): Int = Objects.hash(token) 34 | 35 | override fun equals(other: Any?): Boolean = other is Bearer && other.token == token 36 | 37 | override fun toString(): String = "Authentication.Bearer(token=$token)" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/editor/AvatarPickerContentLayout.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.editor 2 | 3 | import android.os.Parcelable 4 | import kotlinx.parcelize.Parcelize 5 | 6 | /** 7 | * The layout direction of the Avatar picker in the Quick Editor. 8 | */ 9 | @Parcelize 10 | public sealed class AvatarPickerContentLayout : Parcelable { 11 | /** 12 | * Horizontal layout. 13 | */ 14 | @Parcelize 15 | public data object Horizontal : AvatarPickerContentLayout() 16 | 17 | /** 18 | * Vertical layout. 19 | */ 20 | @Parcelize 21 | public data object Vertical : AvatarPickerContentLayout() 22 | } 23 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/editor/GravatarQuickEditorDismissReason.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.editor 2 | 3 | /** 4 | * Class representing possible dismiss reasons that could close the GravatarQuickEditor 5 | */ 6 | public sealed class GravatarQuickEditorDismissReason { 7 | /** 8 | * Quick Editor was closed by user 9 | */ 10 | public data object Finished : GravatarQuickEditorDismissReason() 11 | 12 | /** 13 | * Any issue related to the OAuthFlow. 14 | * One of the reasons for the flow to fail are the wrong OAuthParams set in the GravatarQuickEditor 15 | */ 16 | public data object OauthFailed : GravatarQuickEditorDismissReason() 17 | 18 | /** 19 | * This reason will be used when an invalid/expired token is provided in the GravatarQuickEditor 20 | */ 21 | public data object InvalidToken : GravatarQuickEditorDismissReason() 22 | } 23 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/editor/GravatarUiMode.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.editor 2 | 3 | /** 4 | * The color scheme to be used in the Quick Editor. 5 | */ 6 | public enum class GravatarUiMode { 7 | LIGHT, 8 | DARK, 9 | SYSTEM, 10 | } 11 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/editor/QuickEditorAction.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.editor 2 | 3 | internal sealed class QuickEditorAction { 4 | data object DismissEditor : QuickEditorAction() 5 | 6 | data object NotifyDismissIgnored : QuickEditorAction() 7 | } 8 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/editor/QuickEditorEvent.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.editor 2 | 3 | import com.gravatar.restapi.models.Profile 4 | 5 | internal sealed class QuickEditorEvent { 6 | data object Refresh : QuickEditorEvent() 7 | 8 | data object UpdateAvatarCache : QuickEditorEvent() 9 | 10 | class OnProfileUpdated(val profile: Profile) : QuickEditorEvent() 11 | 12 | data object OnEditAvatarClicked : QuickEditorEvent() 13 | 14 | data object OnEditAboutClicked : QuickEditorEvent() 15 | 16 | data object OnConfirmDismissal : QuickEditorEvent() 17 | 18 | data class OnCompactWindowEnabled(val enabled: Boolean) : QuickEditorEvent() 19 | 20 | class OnAboutEditorUnsavedChangesUpdated(val unsavedChanges: Boolean) : QuickEditorEvent() 21 | 22 | data object OnUnsavedChangesKeepEditingClicked : QuickEditorEvent() 23 | 24 | data object OnUnsavedChangesExitClicked : QuickEditorEvent() 25 | } 26 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/editor/QuickEditorPage.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.editor 2 | 3 | internal enum class QuickEditorPage { 4 | AvatarPicker, 5 | AboutEditor, 6 | } 7 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/editor/QuickEditorUiState.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.editor 2 | 3 | import com.gravatar.restapi.models.Profile 4 | import com.gravatar.types.Email 5 | import com.gravatar.ui.components.ComponentState 6 | 7 | internal data class QuickEditorUiState( 8 | val email: Email, 9 | val page: QuickEditorPage, 10 | val pageNavigationEnabled: Boolean, 11 | val profile: ComponentState? = null, 12 | val avatarCacheBuster: Long? = null, 13 | val compactWindow: Boolean = false, 14 | val aboutEditorUnsavedChangesPresent: Boolean = false, 15 | val discardAboutEditorChangesDialogVisible: Boolean = false, 16 | ) { 17 | val editAvatarButtonVisible: Boolean = pageNavigationEnabled && page == QuickEditorPage.AboutEditor 18 | val editAboutButtonVisible: Boolean = pageNavigationEnabled && page == QuickEditorPage.AvatarPicker 19 | } 20 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/navigation/EditorNavDestinations.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.navigation 2 | 3 | internal enum class EditorNavDestinations { 4 | QUICK_EDITOR, 5 | ALT_TEXT, 6 | } 7 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/navigation/QuickEditorPage.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.navigation 2 | 3 | internal enum class QuickEditorPage { 4 | SPLASH, 5 | OAUTH, 6 | EDITOR, 7 | } 8 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/oauth/OAuthUiState.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.oauth 2 | 3 | import com.gravatar.restapi.models.Profile 4 | import com.gravatar.ui.components.ComponentState 5 | 6 | internal data class OAuthUiState( 7 | val status: OAuthStatus = OAuthStatus.LoginRequired, 8 | val profile: ComponentState? = null, 9 | val avatarCacheBuster: String? = null, 10 | ) 11 | 12 | internal sealed class OAuthStatus { 13 | internal data object Authorizing : OAuthStatus() 14 | 15 | internal data object LoginRequired : OAuthStatus() 16 | 17 | internal data object WrongEmailAuthorized : OAuthStatus() 18 | 19 | internal data class EmailAssociatedCheckError(val token: String) : OAuthStatus() 20 | } 21 | 22 | internal sealed class OAuthAction { 23 | internal data object StartOAuth : OAuthAction() 24 | 25 | internal data object AuthorizationSuccess : OAuthAction() 26 | 27 | internal data object AuthorizationFailure : OAuthAction() 28 | } 29 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/oauth/WordPressOauth.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.oauth 2 | 3 | import android.net.Uri 4 | import com.gravatar.types.Email 5 | 6 | internal object WordPressOauth { 7 | fun buildUrl(clientId: String, redirectUri: String, email: Email): String { 8 | return Uri.Builder() 9 | .scheme("https") 10 | .authority("public-api.wordpress.com") 11 | .appendPath("oauth2") 12 | .appendPath("authorize") 13 | .appendQueryParameter("client_id", clientId) 14 | .appendQueryParameter("redirect_uri", redirectUri) 15 | .appendQueryParameter("response_type", "token") 16 | .appendQueryParameter("scope[1]", "auth") 17 | .appendQueryParameter("scope[2]", "gravatar-profile:read") 18 | .appendQueryParameter("scope[3]", "gravatar-profile:manage") 19 | .appendQueryParameter("user_email", email.toString()) 20 | .build() 21 | .toString() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/splash/SplashAction.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.splash 2 | 3 | internal sealed class SplashAction { 4 | data object ShowQuickEditor : SplashAction() 5 | 6 | data object ShowOAuth : SplashAction() 7 | } 8 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/time/Clock.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.time 2 | 3 | internal interface Clock { 4 | fun getTimeMillis(): Long 5 | } 6 | 7 | internal class SystemClock : Clock { 8 | override fun getTimeMillis(): Long { 9 | return System.currentTimeMillis() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/drawable/gravatar_alt_text_help.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/drawable/gravatar_avatar_more_options_alt_text.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 14 | 17 | 21 | 22 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/drawable/gravatar_avatar_more_options_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/drawable/gravatar_avatar_more_options_dots.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/drawable/gravatar_avatar_more_options_download.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/drawable/gravatar_avatar_more_options_rating.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/drawable/gravatar_photo_library.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/drawable/ic_checkmark.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/drawable/ic_edit.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/values/available_languages.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | en_US 6 | ar 7 | de 8 | es 9 | fr 10 | iw 11 | in 12 | it 13 | ja 14 | ko 15 | nl 16 | pt_BR 17 | ru 18 | sv 19 | tr 20 | zh_CN 21 | zh_TW 22 | 23 | 24 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 11 | 12 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/main/res/xml/quickeditor_filepaths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/AvatarTestUtils.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor 2 | 3 | import com.gravatar.restapi.models.Avatar 4 | import java.net.URI 5 | 6 | internal fun createAvatar( 7 | id: String, 8 | isSelected: Boolean? = null, 9 | rating: Avatar.Rating = Avatar.Rating.G, 10 | altText: String = "alt", 11 | url: URI = URI.create("https://gravatar.com/avatar/test"), 12 | ) = Avatar { 13 | imageUrl = url 14 | imageId = id 15 | this.rating = rating 16 | this.altText = altText 17 | updatedDate = "" 18 | selected = isSelected 19 | } 20 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/CoroutineTestRule.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.test.StandardTestDispatcher 6 | import kotlinx.coroutines.test.TestDispatcher 7 | import kotlinx.coroutines.test.resetMain 8 | import kotlinx.coroutines.test.setMain 9 | import org.junit.rules.TestWatcher 10 | import org.junit.runner.Description 11 | 12 | @OptIn(ExperimentalCoroutinesApi::class) 13 | class CoroutineTestRule( 14 | private val testDispatcher: TestDispatcher = StandardTestDispatcher(), 15 | ) : TestWatcher() { 16 | override fun starting(description: Description) { 17 | Dispatchers.setMain(testDispatcher) 18 | } 19 | 20 | override fun finished(description: Description) { 21 | Dispatchers.resetMain() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/GravatarScreenshotTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui 2 | 3 | import androidx.compose.runtime.Composable 4 | import com.gravatar.ui.GravatarTheme 5 | import com.gravatar.uitestutils.RoborazziTest 6 | 7 | internal fun RoborazziTest.gravatarScreenshotTest(composable: @Composable () -> Unit) { 8 | screenshotTest { 9 | GravatarTheme { 10 | composable() 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/StringExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | internal class StringExtensionsTest { 7 | @Test 8 | fun `give a string with spaces when orphans removed then last space replaced with non-breaking`() { 9 | // Given 10 | val input = "This is a test string with spaces" 11 | val expected = "This is a test string with\u00A0spaces" 12 | 13 | // When 14 | val actual = input.removeOrphans 15 | 16 | // Then 17 | assertEquals(expected, actual) 18 | } 19 | 20 | @Test 21 | fun `give a string without spaces when orphans removed then string remains unchanged`() { 22 | // Given 23 | val input = "ThisIsATestStringWithoutSpaces" 24 | 25 | // When 26 | val actual = input.removeOrphans 27 | 28 | // Then 29 | assertEquals(input, actual) 30 | } 31 | 32 | @Test 33 | fun `given an empty string when orphans removed then string remains unchanged`() { 34 | // Given 35 | val input = "" 36 | 37 | // When 38 | val actual = input.removeOrphans 39 | 40 | // Then 41 | assertEquals(input, actual) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/components/AlertBannerTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.compose.ui.res.stringResource 4 | import com.gravatar.quickeditor.R 5 | import com.gravatar.quickeditor.ui.gravatarScreenshotTest 6 | import com.gravatar.uitestutils.RoborazziTest 7 | import org.junit.Test 8 | import org.robolectric.annotation.Config 9 | 10 | class AlertBannerTest : RoborazziTest() { 11 | @Test 12 | fun alertBannerLight() = gravatarScreenshotTest { 13 | AlertBanner(message = stringResource(id = R.string.gravatar_qe_alert_banner_no_avatar_selected), onClose = {}) 14 | } 15 | 16 | @Test 17 | @Config(qualifiers = "+night") 18 | fun alertBannerDark() = gravatarScreenshotTest { 19 | AlertBanner(message = stringResource(id = R.string.gravatar_qe_alert_banner_no_avatar_selected), onClose = {}) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/components/CtaSectionTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.compose.ui.Modifier 4 | import com.gravatar.quickeditor.ui.gravatarScreenshotTest 5 | import com.gravatar.uitestutils.RoborazziTest 6 | import org.junit.Test 7 | import org.robolectric.annotation.Config 8 | 9 | class CtaSectionTest : RoborazziTest() { 10 | @Test 11 | fun ctaSectionLight() = gravatarScreenshotTest { 12 | CtaSection( 13 | title = "Oooops", 14 | message = "Something went wrong and we couldn't connect to Gravatar servers.", 15 | buttonText = "Retry", 16 | onButtonClick = {}, 17 | modifier = Modifier.withBorder(), 18 | ) 19 | } 20 | 21 | @Test 22 | @Config(qualifiers = "+night") 23 | fun ctaSectionDark() = gravatarScreenshotTest { 24 | CtaSection( 25 | title = "Oooops", 26 | message = "Something went wrong and we couldn't connect to Gravatar servers.", 27 | buttonText = "Retry", 28 | onButtonClick = {}, 29 | modifier = Modifier.withBorder(), 30 | ) 31 | } 32 | 33 | @Test 34 | fun ctaSectionNoTitleLight() = gravatarScreenshotTest { 35 | CtaSection( 36 | message = "Manage your profile for the web in one place.", 37 | buttonText = "Continue", 38 | onButtonClick = {}, 39 | modifier = Modifier.withBorder(), 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/components/QESectionMessageTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.compose.foundation.layout.width 4 | import androidx.compose.material3.Surface 5 | import androidx.compose.ui.Modifier 6 | import androidx.compose.ui.unit.dp 7 | import com.gravatar.quickeditor.ui.gravatarScreenshotTest 8 | import com.gravatar.uitestutils.RoborazziTest 9 | import org.junit.Test 10 | 11 | class QESectionMessageTest : RoborazziTest() { 12 | @Test 13 | fun qeTextTestNoOrphan() = gravatarScreenshotTest { 14 | Surface( 15 | modifier = Modifier.width(220.dp), 16 | ) { 17 | QESectionMessage(message = "This is a long text that should not break just before the last word.") 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/components/QETopBarTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import com.gravatar.quickeditor.ui.gravatarScreenshotTest 4 | import com.gravatar.uitestutils.RoborazziTest 5 | import org.junit.Test 6 | import org.robolectric.annotation.Config 7 | 8 | class QETopBarTest : RoborazziTest() { 9 | @Test 10 | fun qrTopBarLight() = gravatarScreenshotTest { 11 | QETopBar(leftButton = { QETopBarTextButton({}) }) 12 | } 13 | 14 | @Test 15 | @Config(qualifiers = "+night") 16 | fun qrTopBarDark() = gravatarScreenshotTest { 17 | QETopBar(leftButton = { QETopBarTextButton({}) }) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/components/UploadImageGridButtonTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.components 2 | 3 | import androidx.compose.foundation.layout.size 4 | import androidx.compose.material3.Surface 5 | import androidx.compose.ui.Modifier 6 | import com.gravatar.quickeditor.ui.gravatarScreenshotTest 7 | import com.gravatar.uitestutils.RoborazziTest 8 | import org.junit.Test 9 | 10 | class UploadImageGridButtonTest : RoborazziTest() { 11 | @Test 12 | fun uploadImageGridButtonEnabled() = gravatarScreenshotTest { 13 | Surface { 14 | UploadImageGridButton( 15 | onTakePhotoClick = {}, 16 | onChoosePhotoClick = {}, 17 | enabled = true, 18 | modifier = Modifier.size(avatarSize), 19 | ) 20 | } 21 | } 22 | 23 | @Test 24 | fun uploadImageGridButtonDisabled() = gravatarScreenshotTest { 25 | Surface { 26 | UploadImageGridButton( 27 | onTakePhotoClick = {}, 28 | onChoosePhotoClick = {}, 29 | enabled = false, 30 | modifier = Modifier.size(avatarSize), 31 | ) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/oauth/OAuthPageTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.quickeditor.ui.oauth 2 | 3 | import com.gravatar.quickeditor.ui.gravatarScreenshotTest 4 | import com.gravatar.types.Email 5 | import com.gravatar.ui.components.ComponentState 6 | import com.gravatar.uitestutils.RoborazziTest 7 | import org.junit.Test 8 | import org.robolectric.annotation.Config 9 | 10 | class OAuthPageTest : RoborazziTest() { 11 | @Test 12 | fun oAuthPageLight() = gravatarScreenshotTest { 13 | OauthPage( 14 | uiState = OAuthUiState( 15 | profile = ComponentState.Empty, 16 | ), 17 | email = Email("email"), 18 | onStartOAuthClicked = {}, 19 | onDoneClicked = {}, 20 | ) 21 | } 22 | 23 | @Test 24 | @Config(qualifiers = "+night") 25 | fun oAuthPageDark() { 26 | gravatarScreenshotTest { 27 | OauthPage( 28 | uiState = OAuthUiState( 29 | profile = ComponentState.Empty, 30 | ), 31 | email = Email("email"), 32 | onStartOAuthClicked = {}, 33 | onDoneClicked = {}, 34 | ) 35 | } 36 | } 37 | 38 | @Test 39 | fun oAuthPageAuthorizing() = gravatarScreenshotTest { 40 | OauthPage( 41 | uiState = OAuthUiState( 42 | profile = ComponentState.Empty, 43 | status = OAuthStatus.Authorizing, 44 | ), 45 | email = Email("email"), 46 | onStartOAuthClicked = {}, 47 | onDoneClicked = {}, 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /gravatar-ui/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /gravatar-ui/GravatarUi.md: -------------------------------------------------------------------------------- 1 | # Module gravatar-ui 2 | This module contains the UI components for the Gravatar SDK. 3 | 4 | # Package com.gravatar.ui 5 | This package includes the UI components provided with the Gravatar SDK. 6 | -------------------------------------------------------------------------------- /gravatar-ui/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.dokka.gradle.DokkaTaskPartial 2 | 3 | plugins { 4 | alias(libs.plugins.gravatar.android.library) 5 | alias(libs.plugins.gravatar.android.compose) 6 | alias(libs.plugins.dokka) 7 | alias(libs.plugins.gravatar.maven.publish) 8 | } 9 | 10 | android { 11 | namespace = "com.gravatar.ui" 12 | 13 | tasks.withType().configureEach { 14 | dokkaSourceSets { 15 | configureEach { 16 | includes.from("GravatarUi.md") 17 | } 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation(libs.coil.compose) 24 | implementation(libs.coil.svg) 25 | implementation(project(":gravatar")) 26 | 27 | // Jetpack Compose 28 | implementation(platform(libs.compose.bom)) 29 | implementation(libs.compose.ui) 30 | implementation(libs.compose.ui.tooling.preview) 31 | implementation(libs.compose.material3) 32 | debugImplementation(libs.androidx.compose.ui.tooling) 33 | 34 | testImplementation(libs.junit) 35 | testImplementation(project(":uitestutils")) 36 | } 37 | -------------------------------------------------------------------------------- /gravatar-ui/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/consumer-rules.pro -------------------------------------------------------------------------------- /gravatar-ui/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 -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.legacyProfileLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.legacyProfileLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.legacyProfileLoadingLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.legacyProfileLoadingLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileDark.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileLoadingDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileLoadingDark.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileLoadingLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileLoadingLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileWithCustomComponents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileSummaryTest.profileWithCustomComponents.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.legacyProfileLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.legacyProfileLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.legacyProfileLoadingLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.legacyProfileLoadingLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.legacyProfileWithoutDescription.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.legacyProfileWithoutDescription.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileDark.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileLoadingDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileLoadingDark.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileLoadingLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileLoadingLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileWithCustomComponents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileWithCustomComponents.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileWithoutDescription.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.LargeProfileTest.profileWithoutDescription.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.legacyProfileLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.legacyProfileLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.legacyProfileLoadingLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.legacyProfileLoadingLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileDark.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileEmptyStateDescription.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileEmptyStateDescription.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileLoadingDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileLoadingDark.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileLoadingLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileLoadingLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileWithCustomComponents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileWithCustomComponents.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileWithoutLocation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileSummaryTest.profileWithoutLocation.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.legacyProfileLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.legacyProfileLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.legacyProfileLoadingLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.legacyProfileLoadingLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileDark.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileLoadingDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileLoadingDark.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileLoadingLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileLoadingLight.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileWithCustomComponents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileWithCustomComponents.png -------------------------------------------------------------------------------- /gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileWithEmptyDescription.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar-ui/screenshotTests/roborazzi/com.gravatar.gravatar.ui.components.ProfileTest.profileWithEmptyDescription.png -------------------------------------------------------------------------------- /gravatar-ui/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/java/com/gravatar/ui/components/ProfileComponentsUtils.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.ui.components 2 | 3 | import androidx.compose.foundation.clickable 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.platform.LocalUriHandler 8 | import com.gravatar.GravatarConstants 9 | import com.gravatar.restapi.models.Profile 10 | 11 | @Composable 12 | internal fun EmptyProfileClickableContainer( 13 | userProfileState: ComponentState?, 14 | content: @Composable () -> Unit, 15 | ) { 16 | if (userProfileState is ComponentState.Empty) { 17 | Box(Modifier.emptyProfileClick(userProfileState)) { 18 | content() 19 | } 20 | } else { 21 | content() 22 | } 23 | } 24 | 25 | @Composable 26 | private fun Modifier.emptyProfileClick(userProfileState: ComponentState?): Modifier { 27 | return if (userProfileState is ComponentState.Empty) { 28 | val uriHandler = LocalUriHandler.current 29 | this.clickable { 30 | uriHandler.openUri(GravatarConstants.GRAVATAR_SIGN_IN_URL) 31 | } 32 | } else { 33 | this 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/java/com/gravatar/ui/components/UiUtils.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.ui.components 2 | 3 | import android.content.res.Configuration 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.ui.platform.LocalConfiguration 6 | 7 | @Composable 8 | internal fun isNightModeEnabled() = 9 | (LocalConfiguration.current.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES 10 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/java/com/gravatar/ui/theme/ColorScheme.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.ui.theme 2 | 3 | import androidx.compose.material3.darkColorScheme 4 | import androidx.compose.material3.lightColorScheme 5 | 6 | internal fun gravatarLightColorScheme() = lightColorScheme( 7 | primary = ColorsPalette.Primary50, 8 | onPrimary = ColorsPalette.Neutral100, 9 | secondary = ColorsPalette.Secondary50, 10 | onSecondary = ColorsPalette.Neutral100, 11 | tertiary = ColorsPalette.Neutral70, 12 | onTertiary = ColorsPalette.Neutral100, 13 | error = ColorsPalette.Error50, 14 | onError = ColorsPalette.Error70, 15 | errorContainer = ColorsPalette.Error70, 16 | onErrorContainer = ColorsPalette.Secondary50, 17 | surfaceDim = ColorsPalette.Neutral90, 18 | surface = ColorsPalette.Neutral100, 19 | onSurface = ColorsPalette.Neutral50, 20 | ) 21 | 22 | internal fun gravatarDarkColorScheme() = darkColorScheme( 23 | primary = ColorsPalette.Primary50, 24 | onPrimary = ColorsPalette.Neutral100, 25 | secondary = ColorsPalette.Secondary100, 26 | onSecondary = ColorsPalette.Neutral50, 27 | tertiary = ColorsPalette.Neutral90, 28 | onTertiary = ColorsPalette.Neutral100, 29 | error = ColorsPalette.Error50, 30 | onError = ColorsPalette.Error70, 31 | errorContainer = ColorsPalette.Error70, 32 | onErrorContainer = ColorsPalette.Secondary50, 33 | surfaceDim = ColorsPalette.Neutral70, 34 | surface = ColorsPalette.Neutral50, 35 | onSurface = ColorsPalette.Secondary100, 36 | ) 37 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/java/com/gravatar/ui/theme/ColorsPalette.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | internal object ColorsPalette { 6 | val Primary30 = Color(0xFF1D2EC4) 7 | val Primary50 = Color(0xFF1D4FC4) 8 | val Secondary50 = Color(0xFF101517) 9 | val Secondary60 = Color(0xFF282D32) 10 | val Secondary70 = Color(0xFF50575E) 11 | val Secondary90 = Color(0xFFF0F0F0) 12 | val Secondary100 = Color(0xFFFFFFFF) 13 | val Error50 = Color(0xFFCC1818) 14 | val Error70 = Color(0xFFF4A2A2) 15 | val Neutral50 = Color(0xFF101517) 16 | val Neutral60 = Color(0xFF282D32) 17 | val Neutral70 = Color(0xFF50575E) 18 | val Neutral90 = Color(0xFFF0F0F0) 19 | val Neutral100 = Color(0xFFFFFFFF) 20 | } 21 | 22 | internal val skeleton_dark_start_color: Color = Color(0xFF6B6B6B) 23 | internal val skeleton_dark_end_color: Color = Color(0xFF979797) 24 | internal val skeleton_light_start_color: Color = Color(0xFFE5E7E9) 25 | internal val skeleton_light_end_color: Color = Color(0xFFF2F2F2) 26 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_foursquare_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_github_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_gravatar_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_instagram_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_mastodongeneric_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_stackoverflow_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_tiktok_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_tumblr_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_twitch_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_twitter_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_vimeo_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/drawable/gravatar_wordpress_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-ar/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | صورة الملف الشخصي للمستخدم 10 | الموقع 11 | المطالبة بالملف الشخصي 12 | إضافة موقعك وضمائرك وما إلى ذلك 13 | اسمك 14 | أخبر العالم مَن أنت. صورتك الرمزية وسيرتك الذاتية التي تتبعك عبر الويب. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | Benutzerprofilbild 10 | Ort 11 | Profil beanspruchen 12 | Ort, Pronomen usw. hinzufügen 13 | Dein Name 14 | Zeig der Welt, wer du bist. Mit einem Avatar und einer Biografie, die dich überall im Web begleiten. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | Imagen de perfil del usuario 10 | Ubicación 11 | Reclamar perfil 12 | Añade tu ubicación, pronombres, etc. 13 | Tu nombre 14 | Dile al mundo quién eres. Tu avatar y biografía que te siguen por toda la web. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | Image de profil de l’utilisateur 10 | Localisation 11 | Demander un profil 12 | Ajoutez votre emplacement, vos pronoms, etc. 13 | Votre nom 14 | Faites savoir au monde qui vous êtes. Votre avatar et votre bio qui vous suivent partout sur le Web. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-in/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | Gambar profil pengguna 10 | Lokasi 11 | Klaim profil 12 | Masukkan lokasi, kata ganti Anda, dll. 13 | Nama Anda 14 | Perkenalkan diri Anda. Avatar dan bio yang menyertai aktivitas online Anda. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | Immagine profilo utente 10 | Posizione 11 | Rivendica profilo 12 | Aggiungi posizione, pronomi, ecc 13 | Il tuo nome 14 | Racconta chi sei a tutto il mondo. L\'avatar e la bio che ti accompagnano in tutto il web. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-iw/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | תמונת פרופיל של המשתמש 10 | מיקום 11 | לקבל בעלות על הפרופיל 12 | להוסיף מיקום, כינויי גוף וכו\' 13 | השם שלך 14 | כל העולם רוצה להכיר אותך. צלמית המשתמש והביו שלך הולכים איתך לכל מקום באינטרנט. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | ユーザープロフィール画像 10 | 場所 11 | プロフィールを取得 12 | 場所や代名詞などを追加 13 | あなたの名前 14 | あなたのことを紹介しましょう。 ウェブ全体であなたを表すアバターとプロフィールです。 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-ko/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 사용자 프로필 이미지 10 | 위치 11 | 프로필 신청 12 | 위치, 대명사 등 추가 13 | 이름 14 | 회원님을 소개하세요. 웹에서 나를 따라다니는 아바타와 약력입니다. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-nl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | Gebruiker profiel afbeelding 10 | Locatie 11 | Profiel claimen 12 | Je locatie, persoonlijk voornaamwoorden, etc. toevoegen 13 | Je naam 14 | Vertel de wereld wie je bent. Je avatar en bio die het hele web met je overgaan. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-pt-rBR/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | Imagem do perfil do usuário 10 | Localização 11 | Reivindicar perfil 12 | Adicione sua localização, pronomes, etc. 13 | Seu nome 14 | Compartilhe um pouco sobre você com o mundo. Seu avatar e sua bio por toda Internet. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | Изображение профиля пользователя 10 | Местоположение 11 | Зарегистрировать профиль 12 | Добавьте ваше местоположение, местоимения и т. д. 13 | Ваше имя 14 | Расскажите о себе. Ваш аватар и визитка, которые сопровождают вас повсюду в Интернете. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-sv/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | Användarprofilsbild 10 | Plats 11 | Gör anspråk på profil 12 | Lägg till din plats, pronomen etc. 13 | Ditt namn 14 | Berätta för världen vem du är. Din profilbild och bio som följer dig över hela webben. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-tr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | Kullanıcı profil görseli 10 | Konum 11 | Profili sahiplen 12 | Konumunuzu, zamirlerinizi vb. ekleyin 13 | İsminiz 14 | Kim olduğunuzu dünyaya anlatın. Web\'de gösterilen avatarınız ve biyografiniz. 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 用户个人资料图片 10 | 位置 11 | 申请个人资料 12 | 添加您的位置、性别代词等信息 13 | 您的姓名 14 | 让大家认识您。 您应用于整个网络的头像和简介。 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values-zh-rTW/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 使用者個人檔案圖片 10 | 地點 11 | 登記個人檔案 12 | 新增你的位置、性別認同代稱等 13 | 你的顯示名稱 14 | 讓全世界認識你。 網路世界的隨身大頭貼與簡介。 15 | 16 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values/available_languages.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | en_US 6 | ar 7 | de 8 | es 9 | fr 10 | iw 11 | in 12 | it 13 | ja 14 | ko 15 | nl 16 | pt_BR 17 | ru 18 | sv 19 | tr 20 | zh_CN 21 | zh_TW 22 | 23 | 24 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | View profile 4 | Tell the world who you are. Your avatar and bio that follows you across the web. 5 | Your Name 6 | Add your location, pronouns, etc 7 | Claim profile 8 | Location 9 | User profile image 10 | 11 | -------------------------------------------------------------------------------- /gravatar-ui/src/main/res/values/values.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 800 4 | -------------------------------------------------------------------------------- /gravatar-ui/src/test/java/com/gravatar/gravatar/ui/GravatarScreenshotTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.gravatar.ui 2 | 3 | import androidx.compose.runtime.Composable 4 | import com.gravatar.ui.GravatarTheme 5 | import com.gravatar.uitestutils.RoborazziTest 6 | 7 | internal fun RoborazziTest.gravatarScreenshotTest(composable: @Composable () -> Unit) { 8 | screenshotTest { 9 | GravatarTheme { 10 | composable() 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gravatar/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /gravatar/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /gravatar/.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10.12.5 5 | JavaOnly 6 | 14 | 15 | -------------------------------------------------------------------------------- /gravatar/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /gravatar/.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /gravatar/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 13 | -------------------------------------------------------------------------------- /gravatar/GravatarCore.md: -------------------------------------------------------------------------------- 1 | # Module gravatar 2 | The core module of the Gravatar SDK. 3 | 4 | # Package com.gravatar 5 | This package contains the main classes of the Gravatar SDK. 6 | 7 | # Package com.gravatar.api.models 8 | This package contains the models used in the Gravatar API. -------------------------------------------------------------------------------- /gravatar/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.dokka.gradle.DokkaTaskPartial 2 | 3 | plugins { 4 | alias(libs.plugins.gravatar.android.library) 5 | alias(libs.plugins.gravatar.openapi.generator) 6 | alias(libs.plugins.parcelize) 7 | alias(libs.plugins.dokka) 8 | alias(libs.plugins.ksp) 9 | alias(libs.plugins.gravatar.maven.publish) 10 | } 11 | 12 | val sdkVersion: String by rootProject.extra 13 | 14 | android { 15 | namespace = "com.gravatar" 16 | buildFeatures.buildConfig = true 17 | 18 | defaultConfig { 19 | buildConfigField("String", "SDK_VERSION", "\"$sdkVersion\"") 20 | } 21 | 22 | testOptions { 23 | unitTests { 24 | isIncludeAndroidResources = true 25 | } 26 | } 27 | 28 | tasks.withType().configureEach { 29 | dokkaSourceSets { 30 | configureEach { 31 | includes.from("GravatarCore.md") 32 | } 33 | } 34 | } 35 | 36 | detekt { 37 | tasks.withType().configureEach { 38 | exclude("**/restapi/**") // Specify the directory to exclude 39 | } 40 | } 41 | } 42 | 43 | dependencies { 44 | api(libs.okhttp) 45 | implementation(libs.moshi) 46 | implementation(libs.kotlinx.coroutines) 47 | ksp(libs.moshi.kotlin.codegen) 48 | 49 | testImplementation(libs.junit) 50 | testImplementation(libs.mockk.android) 51 | testImplementation(libs.mockk.agent) 52 | testImplementation(libs.kotlinx.coroutines.test) 53 | testImplementation(kotlin("test")) 54 | } 55 | -------------------------------------------------------------------------------- /gravatar/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class com.gravatar.restapi.models.** { *; } 2 | -------------------------------------------------------------------------------- /gravatar/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/gravatar/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gravatar/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gravatar/openapi/templates/infrastructure/ApiAbstractions.kt.mustache: -------------------------------------------------------------------------------- 1 | package {{packageName}}.infrastructure 2 | 3 | internal typealias MultiValueMap = MutableMap> 4 | 5 | internal fun collectionDelimiter(collectionFormat: String): String = when(collectionFormat) { 6 | "csv" -> "," 7 | "tsv" -> "\t" 8 | "pipe" -> "|" 9 | "space" -> " " 10 | else -> "" 11 | } 12 | 13 | internal val defaultMultiValueConverter: (item: Any?) -> String = { item -> "$item" } 14 | 15 | internal fun toMultiValue(items: Array, collectionFormat: String, map: (item: T) -> String = defaultMultiValueConverter): List 16 | = toMultiValue(items.asIterable(), collectionFormat, map) 17 | 18 | internal fun toMultiValue(items: Iterable, collectionFormat: String, map: (item: T) -> String = defaultMultiValueConverter): List { 19 | return when(collectionFormat) { 20 | "multi" -> items.map(map) 21 | else -> listOf(items.joinToString(separator = collectionDelimiter(collectionFormat), transform = map)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /gravatar/openapi/templates/infrastructure/PartConfig.kt.mustache: -------------------------------------------------------------------------------- 1 | package {{packageName}}.infrastructure 2 | 3 | /** 4 | * Defines a config object for a given part of a multi-part request. 5 | * NOTE: Headers is a Map because rfc2616 defines 6 | * multi-valued headers as csv-only. 7 | */ 8 | internal data class PartConfig( 9 | val headers: MutableMap = mutableMapOf(), 10 | val body: T? = null 11 | ) 12 | -------------------------------------------------------------------------------- /gravatar/openapi/templates/infrastructure/RequestConfig.kt.mustache: -------------------------------------------------------------------------------- 1 | package {{packageName}}.infrastructure 2 | 3 | /** 4 | * Defines a config object for a given request. 5 | * NOTE: This object doesn't include 'body' because it 6 | * allows for caching of the constructed object 7 | * for many request definitions. 8 | * NOTE: Headers is a Map because rfc2616 defines 9 | * multi-valued headers as csv-only. 10 | */ 11 | internal data class RequestConfig( 12 | val method: RequestMethod, 13 | val path: String, 14 | val headers: MutableMap = mutableMapOf(), 15 | val params: MutableMap = mutableMapOf(), 16 | val query: MutableMap> = mutableMapOf(), 17 | val requiresAuthentication: Boolean, 18 | val body: T? = null 19 | ) 20 | -------------------------------------------------------------------------------- /gravatar/openapi/templates/infrastructure/RequestMethod.kt.mustache: -------------------------------------------------------------------------------- 1 | package {{packageName}}.infrastructure 2 | 3 | /** 4 | * Provides enumerated HTTP verbs 5 | */ 6 | internal enum class RequestMethod { 7 | GET, DELETE, HEAD, OPTIONS, PATCH, POST, PUT 8 | } 9 | -------------------------------------------------------------------------------- /gravatar/openapi/templates/jvm-common/infrastructure/Serializer.kt.mustache: -------------------------------------------------------------------------------- 1 | package {{packageName}}.infrastructure 2 | 3 | {{#moshi}} 4 | import com.squareup.moshi.Moshi 5 | {{#enumUnknownDefaultCase}} 6 | import com.squareup.moshi.adapters.EnumJsonAdapter 7 | {{/enumUnknownDefaultCase}} 8 | {{^moshiCodeGen}} 9 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory 10 | {{/moshiCodeGen}} 11 | {{/moshi}} 12 | 13 | internal object Serializer { 14 | {{#moshi}} 15 | @JvmStatic 16 | internal val moshiBuilder: Moshi.Builder = Moshi.Builder() 17 | .add(URIAdapter()) 18 | {{^moshiCodeGen}} 19 | .add(KotlinJsonAdapterFactory()) 20 | {{/moshiCodeGen}} 21 | 22 | @JvmStatic 23 | internal val moshi: Moshi by lazy { 24 | {{#enumUnknownDefaultCase}} 25 | SerializerHelper.addEnumUnknownDefaultCase(moshiBuilder) 26 | {{/enumUnknownDefaultCase}} 27 | moshiBuilder.build() 28 | } 29 | {{/moshi}} 30 | } 31 | -------------------------------------------------------------------------------- /gravatar/openapi/templates/jvm-common/infrastructure/URIAdapter.kt.mustache: -------------------------------------------------------------------------------- 1 | package {{packageName}}.infrastructure 2 | 3 | {{#kotlinx_serialization}} 4 | import kotlinx.serialization.KSerializer 5 | import kotlinx.serialization.encoding.Decoder 6 | import kotlinx.serialization.encoding.Encoder 7 | import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor 8 | import kotlinx.serialization.descriptors.PrimitiveKind 9 | import kotlinx.serialization.descriptors.SerialDescriptor 10 | {{/kotlinx_serialization}} 11 | {{#moshi}} 12 | import com.squareup.moshi.FromJson 13 | import com.squareup.moshi.ToJson 14 | {{/moshi}} 15 | import java.net.URI 16 | 17 | {{#moshi}} 18 | internal class URIAdapter { 19 | @ToJson 20 | {{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun toJson(uri: URI): String = uri.toString() 21 | 22 | @FromJson 23 | {{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun fromJson(s: String): URI = URI.create(s) 24 | } 25 | {{/moshi}} 26 | {{#kotlinx_serialization}} 27 | internal object URIAdapter : KSerializer { 28 | override fun serialize(encoder: Encoder, value: URI) { 29 | encoder.encodeString(value.toASCIIString()) 30 | } 31 | 32 | override fun deserialize(decoder: Decoder): URI = URI(decoder.decodeString()) 33 | 34 | override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("URI", PrimitiveKind.STRING) 35 | } 36 | {{/kotlinx_serialization}} 37 | -------------------------------------------------------------------------------- /gravatar/openapi/templates/libraries/jvm-okhttp/infrastructure/ApiResponse.kt.mustache: -------------------------------------------------------------------------------- 1 | package {{packageName}}.infrastructure 2 | 3 | import okhttp3.ResponseBody 4 | 5 | internal data class ApiResponse( 6 | val rawResponse: okhttp3.Response, 7 | val body: T?, 8 | val errorBody: ResponseBody?, 9 | ) { 10 | 11 | val isSuccessful = rawResponse.isSuccessful 12 | 13 | val code = rawResponse.code 14 | } 15 | -------------------------------------------------------------------------------- /gravatar/openapi/templates/licenseInfo.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Please note: 4 | * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 5 | * Do not edit this file manually. 6 | * 7 | */ -------------------------------------------------------------------------------- /gravatar/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 -------------------------------------------------------------------------------- /gravatar/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/Gravatar.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar 2 | 3 | import android.content.Context 4 | import android.content.pm.PackageManager 5 | import com.gravatar.di.container.GravatarSdkContainer 6 | 7 | /** 8 | * Entry point for initializing the Gravatar SDK. 9 | */ 10 | public object Gravatar { 11 | /** 12 | * Initializes the Gravatar SDK with the given API key. 13 | * 14 | * @param apiKey The API key to use when making requests to the Gravatar backend. 15 | */ 16 | public fun apiKey(apiKey: String): Gravatar { 17 | GravatarSdkContainer.instance.apiKey = apiKey 18 | return this 19 | } 20 | 21 | /** 22 | * Initializes the Gravatar SDK with the given context. 23 | * The context is used to get the application name. 24 | * 25 | * @param context The context from the app. 26 | */ 27 | public fun context(context: Context): Gravatar { 28 | GravatarSdkContainer.instance.appName = getApplicationName(context) 29 | return this 30 | } 31 | 32 | private fun getApplicationName(context: Context): String { 33 | val applicationInfo = context.packageManager.getApplicationInfo( 34 | context.packageName, 35 | PackageManager.GET_META_DATA, 36 | ) 37 | return context.packageManager.getApplicationLabel(applicationInfo).toString() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/GravatarConstants.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar 2 | 3 | /** 4 | * Gravatar constants 5 | */ 6 | public object GravatarConstants { 7 | /** Gravatar base host */ 8 | internal const val GRAVATAR_BASE_HOST = "gravatar.com" 9 | 10 | /** Gravatar host */ 11 | internal const val GRAVATAR_WWW_BASE_HOST = "www.gravatar.com" 12 | 13 | /** Gravatar API base URL - V3 */ 14 | internal const val GRAVATAR_API_BASE_URL_V3 = "https://api.gravatar.com/v3/" 15 | 16 | /** Gravatar base host */ 17 | public const val GRAVATAR_BASE_URL: String = "https://gravatar.com" 18 | 19 | /** Gravatar Sign-in URL */ 20 | public const val GRAVATAR_SIGN_IN_URL: String = "$GRAVATAR_BASE_URL/profile" 21 | } 22 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/HttpResponseCode.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar 2 | 3 | internal object HttpResponseCode { 4 | /** HTTP client timeout code */ 5 | const val HTTP_CLIENT_TIMEOUT = 408 6 | const val HTTP_NOT_FOUND = 404 7 | const val HTTP_TOO_MANY_REQUESTS = 429 8 | const val UNAUTHORIZED = 401 9 | const val INVALID_REQUEST = 400 10 | const val CONTENT_TOO_LARGE = 413 11 | 12 | private const val HTTP_INTERNAL_ERROR = 500 13 | private const val NETWORK_CONNECT_TIMEOUT_ERROR = 599 14 | 15 | /** Server error codes (5xx) */ 16 | val SERVER_ERRORS = HTTP_INTERNAL_ERROR..NETWORK_CONNECT_TIMEOUT_ERROR 17 | } 18 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/ImageRating.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar 2 | 3 | /** 4 | * Gravatar allows users to self-rate their images so that they can indicate if an image is appropriate 5 | * for a certain audience. By default, only ‘General’ rated images are displayed unless you indicate that 6 | * you would like to see higher ratings. 7 | * 8 | * @property rating The rating to be used in the Gravatar URL. 9 | */ 10 | public enum class ImageRating(public val rating: String) { 11 | /** Suitable for display on all websites with any audience type. */ 12 | General("g"), 13 | 14 | /** May contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence. */ 15 | ParentalGuidance("pg"), 16 | 17 | /** May contain such things as harsh profanity, intense violence, nudity, or hard drug use. */ 18 | Restricted("r"), 19 | 20 | /** May contain hardcore sexual imagery or extremely disturbing violence. */ 21 | X("x"), 22 | } 23 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/ProfileUrl.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar 2 | 3 | import com.gravatar.types.Email 4 | import com.gravatar.types.Hash 5 | import java.net.URL 6 | 7 | /** 8 | * Represents a Gravatar profile URL. 9 | * 10 | * @property hash The hash of the email address. 11 | */ 12 | public class ProfileUrl(public val hash: Hash) { 13 | /** 14 | * The URL of the profile. 15 | */ 16 | public val url: URL = URL("https", GravatarConstants.GRAVATAR_BASE_HOST, hash.toString()) 17 | 18 | public constructor(email: Email) : this(email.hash()) 19 | 20 | /** 21 | * Get the [AvatarUrl] for the represented profile. 22 | * 23 | * @return the [AvatarUrl] of the avatar image 24 | */ 25 | public fun avatarUrl(): AvatarUrl { 26 | return AvatarUrl(hash) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/logger/Logger.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.logger 2 | 3 | import android.util.Log 4 | 5 | internal object Logger { 6 | fun i(tag: String, message: String) = Log.i(tag, message) 7 | 8 | fun w(tag: String, message: String) = Log.w(tag, message) 9 | 10 | fun e(tag: String, message: String) = Log.e(tag, message) 11 | } 12 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/moshiadapers/URIJsonAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.moshiadapers 2 | 3 | import com.squareup.moshi.FromJson 4 | import com.squareup.moshi.ToJson 5 | import java.net.URI 6 | 7 | internal class URIJsonAdapter { 8 | @ToJson 9 | public fun toJson(uri: URI): String { 10 | return uri.toString() 11 | } 12 | 13 | @FromJson 14 | public fun fromJson(uriString: String): URI { 15 | return URI(uriString) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/restapi/infrastructure/ApiAbstractions.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.restapi.infrastructure 2 | 3 | internal typealias MultiValueMap = MutableMap> 4 | 5 | internal fun collectionDelimiter(collectionFormat: String): String = when (collectionFormat) { 6 | "csv" -> "," 7 | "tsv" -> "\t" 8 | "pipe" -> "|" 9 | "space" -> " " 10 | else -> "" 11 | } 12 | 13 | internal val defaultMultiValueConverter: (item: Any?) -> String = { item -> "$item" } 14 | 15 | internal fun toMultiValue( 16 | items: Array, 17 | collectionFormat: String, 18 | map: (item: T) -> String = defaultMultiValueConverter, 19 | ): List = toMultiValue( 20 | items.asIterable(), 21 | collectionFormat, 22 | map, 23 | ) 24 | 25 | internal fun toMultiValue( 26 | items: Iterable, 27 | collectionFormat: String, 28 | map: (item: T) -> String = defaultMultiValueConverter, 29 | ): List { 30 | return when (collectionFormat) { 31 | "multi" -> items.map(map) 32 | else -> listOf(items.joinToString(separator = collectionDelimiter(collectionFormat), transform = map)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/restapi/infrastructure/ApiResponse.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.restapi.infrastructure 2 | 3 | import okhttp3.ResponseBody 4 | 5 | internal data class ApiResponse( 6 | val rawResponse: okhttp3.Response, 7 | val body: T?, 8 | val errorBody: ResponseBody?, 9 | ) { 10 | val isSuccessful = rawResponse.isSuccessful 11 | 12 | val code = rawResponse.code 13 | } 14 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/restapi/infrastructure/PartConfig.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.restapi.infrastructure 2 | 3 | /** 4 | * Defines a config object for a given part of a multi-part request. 5 | * NOTE: Headers is a Map because rfc2616 defines 6 | * multi-valued headers as csv-only. 7 | */ 8 | internal data class PartConfig( 9 | val headers: MutableMap = mutableMapOf(), 10 | val body: T? = null, 11 | ) 12 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/restapi/infrastructure/RequestConfig.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.restapi.infrastructure 2 | 3 | /** 4 | * Defines a config object for a given request. 5 | * NOTE: This object doesn't include 'body' because it 6 | * allows for caching of the constructed object 7 | * for many request definitions. 8 | * NOTE: Headers is a Map because rfc2616 defines 9 | * multi-valued headers as csv-only. 10 | */ 11 | internal data class RequestConfig( 12 | val method: RequestMethod, 13 | val path: String, 14 | val headers: MutableMap = mutableMapOf(), 15 | val params: MutableMap = mutableMapOf(), 16 | val query: MutableMap> = mutableMapOf(), 17 | val requiresAuthentication: Boolean, 18 | val body: T? = null, 19 | ) 20 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/restapi/infrastructure/RequestMethod.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.restapi.infrastructure 2 | 3 | /** 4 | * Provides enumerated HTTP verbs 5 | */ 6 | internal enum class RequestMethod { 7 | GET, 8 | DELETE, 9 | HEAD, 10 | OPTIONS, 11 | PATCH, 12 | POST, 13 | PUT, 14 | } 15 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/restapi/infrastructure/Serializer.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.restapi.infrastructure 2 | 3 | import com.squareup.moshi.Moshi 4 | 5 | internal object Serializer { 6 | @JvmStatic 7 | internal val moshiBuilder: Moshi.Builder = Moshi.Builder() 8 | .add(URIAdapter()) 9 | 10 | @JvmStatic 11 | internal val moshi: Moshi by lazy { 12 | moshiBuilder.build() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/restapi/infrastructure/URIAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.restapi.infrastructure 2 | 3 | import com.squareup.moshi.FromJson 4 | import com.squareup.moshi.ToJson 5 | import java.net.URI 6 | 7 | internal class URIAdapter { 8 | @ToJson 9 | fun toJson(uri: URI): String = uri.toString() 10 | 11 | @FromJson 12 | fun fromJson(s: String): URI = URI.create(s) 13 | } 14 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/services/GravatarException.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.services 2 | 3 | /** 4 | * Exception that will be thrown when an error occurs during Gravatar operations. 5 | * 6 | * @property errorType The type of error that occurred. 7 | * @property originalException The original exception that caused this exception. 8 | */ 9 | public class GravatarException internal constructor( 10 | public val errorType: ErrorType, 11 | public val originalException: Exception? = null, 12 | ) : RuntimeException() 13 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/services/GravatarListener.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.services 2 | 3 | /** 4 | * Generic Listener for Gravatar API call 5 | */ 6 | public interface GravatarListener { 7 | /** 8 | * Called when the Gravatar API call is successful 9 | */ 10 | public fun onSuccess(response: T) 11 | 12 | /** 13 | * Called when the Gravatar API call fails 14 | * 15 | * @param errorType The type of error that occurred 16 | */ 17 | public fun onError(errorType: E) 18 | } 19 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/services/GravatarResult.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.services 2 | 3 | /** 4 | * Class representing the result of a network operation. 5 | */ 6 | public sealed class GravatarResult { 7 | /** 8 | * Represents a successful fetch operation. 9 | * 10 | * @param T The type of the value fetched 11 | * @param E The error type 12 | * @property value The fetched value 13 | */ 14 | public data class Success(public val value: T) : GravatarResult() 15 | 16 | /** 17 | * Represents a failed fetch operation. 18 | * 19 | * @param T The type of the value fetched 20 | * @param E The error type 21 | * @property error The error that occurred 22 | */ 23 | public data class Failure(public val error: E) : GravatarResult() 24 | 25 | /** 26 | * Shortcut function to ignore a failure. 27 | * 28 | * @return The value if the result is a [Success] or null otherwise 29 | */ 30 | public fun valueOrNull(): T? = when (this) { 31 | is Success -> value 32 | is Failure -> null 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/services/HttpException.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.services 2 | 3 | import com.gravatar.restapi.infrastructure.ApiResponse 4 | 5 | /** 6 | * Exception thrown when an HTTP error occurs in a non-cathing method. 7 | * 8 | * [code] The HTTP status code. 9 | * [message] The HTTP status message. 10 | */ 11 | public class HttpException internal constructor(response: ApiResponse<*>) : RuntimeException() { 12 | internal val rawErrorBody: String? = response.errorBody?.string() 13 | public val code: Int = response.code 14 | public override val message: String = "HTTP $code $rawErrorBody" 15 | } 16 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/services/ServiceUtils.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.services 2 | 3 | import com.gravatar.di.container.GravatarSdkContainer 4 | import kotlinx.coroutines.CancellationException 5 | 6 | internal inline fun runCatchingRequest(block: () -> T?): GravatarResult { 7 | @Suppress("TooGenericExceptionCaught") 8 | return try { 9 | val result = block() 10 | if (result != null) { 11 | GravatarResult.Success(result) 12 | } else { 13 | GravatarResult.Failure(ErrorType.NotFound) 14 | } 15 | } catch (cancellationException: CancellationException) { 16 | throw cancellationException 17 | } catch (gravatarException: GravatarException) { 18 | GravatarResult.Failure(gravatarException.errorType) 19 | } catch (ex: Exception) { 20 | GravatarResult.Failure(ex.errorType(GravatarSdkContainer.instance.moshi)) 21 | } 22 | } 23 | 24 | @Suppress("ThrowsCount") 25 | internal inline fun runThrowingExceptionRequest(block: () -> T?): T { 26 | @Suppress("TooGenericExceptionCaught") 27 | try { 28 | return block() ?: throw GravatarException(ErrorType.NotFound) 29 | } catch (cancellationException: CancellationException) { 30 | throw cancellationException 31 | } catch (ex: Exception) { 32 | throw GravatarException(ex.errorType(GravatarSdkContainer.instance.moshi), ex) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/services/interceptors/AuthenticationInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.services.interceptors 2 | 3 | import com.gravatar.di.container.GravatarSdkContainer 4 | import okhttp3.Interceptor 5 | import okhttp3.Request 6 | import okhttp3.Response 7 | 8 | internal class AuthenticationInterceptor(private val oauthToken: String? = null) : Interceptor { 9 | override fun intercept(chain: Interceptor.Chain): Response { 10 | return chain.proceed( 11 | chain.request().newBuilder() 12 | .addAuthorizationHeaderIfPresent() 13 | .build(), 14 | ) 15 | } 16 | 17 | private fun Request.Builder.addAuthorizationHeaderIfPresent(): Request.Builder { 18 | return oauthToken?.let { addHeader(it) } ?: GravatarSdkContainer.instance.apiKey?.let { addHeader(it) } ?: this 19 | } 20 | 21 | private fun Request.Builder.addHeader(bearer: String) = addHeader("Authorization", "Bearer $bearer") 22 | } 23 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/services/interceptors/AvatarUploadTimeoutInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.services.interceptors 2 | 3 | import okhttp3.Interceptor 4 | import okhttp3.Response 5 | import java.util.concurrent.TimeUnit 6 | 7 | private const val TIMEOUT_DURATION_MILLIS = 5 * 60 * 1000 // 5 minutes 8 | 9 | /** 10 | * Increases the timeout for the avatar upload request 11 | */ 12 | internal class AvatarUploadTimeoutInterceptor( 13 | private val timeoutMillis: Int = TIMEOUT_DURATION_MILLIS, 14 | ) : Interceptor { 15 | override fun intercept(chain: Interceptor.Chain): Response { 16 | val request = chain.request() 17 | val newChain = if (request.method == "POST" && request.url.encodedPath == "/v3/me/avatars") { 18 | chain.withConnectTimeout(timeoutMillis, TimeUnit.MILLISECONDS) 19 | .withReadTimeout(timeoutMillis, TimeUnit.MILLISECONDS) 20 | .withWriteTimeout(timeoutMillis, TimeUnit.MILLISECONDS) 21 | } else { 22 | chain 23 | } 24 | return newChain.proceed(chain.request().newBuilder().build()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/services/interceptors/SdkVersionInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.services.interceptors 2 | 3 | import com.gravatar.BuildConfig 4 | import com.gravatar.di.container.GravatarSdkContainer 5 | import okhttp3.Interceptor 6 | import okhttp3.Response 7 | 8 | internal class SdkVersionInterceptor : Interceptor { 9 | override fun intercept(chain: Interceptor.Chain): Response { 10 | return chain.proceed( 11 | chain.request().newBuilder().apply { 12 | addHeader("X-Platform", "Android") 13 | addHeader("X-SDK-Version", BuildConfig.SDK_VERSION) 14 | GravatarSdkContainer.instance.appName?.let { addHeader("X-Source", it) } 15 | }.build(), 16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/types/Email.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.types 2 | 3 | import android.os.Parcelable 4 | import kotlinx.parcelize.Parcelize 5 | 6 | /** 7 | * Email address representation. 8 | * 9 | * @param address the email address 10 | */ 11 | @Parcelize 12 | public class Email(private val address: String) : Parcelable { 13 | /** 14 | * Get a Gravatar hash for a given email address. 15 | * 16 | * @return hash that can used to address Gravatar images or profiles 17 | */ 18 | public fun hash(): Hash { 19 | return Hash(address.trim().lowercase().sha256Hash()) 20 | } 21 | 22 | public override fun toString(): String = this.address 23 | } 24 | -------------------------------------------------------------------------------- /gravatar/src/main/java/com/gravatar/types/Hash.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.types 2 | 3 | import java.security.MessageDigest 4 | 5 | /** 6 | * Convert a byte array to a hexadecimal string. 7 | */ 8 | private fun ByteArray.toHex(): String { 9 | return joinToString("") { "%02x".format(it) } 10 | } 11 | 12 | /** 13 | * Hash a string using SHA-256. 14 | * 15 | * @return SHA-256 hash as a hexadecimal string 16 | */ 17 | public fun String.sha256Hash(): String { 18 | return MessageDigest.getInstance("SHA-256").digest(this.toByteArray()).toHex() 19 | } 20 | 21 | /** 22 | * Gravatar hash. 23 | */ 24 | public class Hash(private val hash: String) { 25 | public override fun toString(): String = this.hash 26 | } 27 | -------------------------------------------------------------------------------- /gravatar/src/test/java/com/gravatar/GravatarTest.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar 2 | 3 | import android.content.Context 4 | import android.content.pm.ApplicationInfo 5 | import android.content.pm.PackageManager 6 | import io.mockk.coVerify 7 | import io.mockk.every 8 | import io.mockk.mockk 9 | import org.junit.Rule 10 | import org.junit.Test 11 | 12 | class GravatarTest { 13 | @get:Rule 14 | var containerRule = GravatarSdkContainerRule() 15 | 16 | @Test 17 | fun `given an api key when initialize method is invoked with it then it should be set internally`() { 18 | val apiKey = "API_KEY" 19 | 20 | Gravatar.apiKey(apiKey) 21 | 22 | coVerify(exactly = 1) { containerRule.gravatarSdkContainerMock.apiKey = apiKey } 23 | } 24 | 25 | @Test 26 | fun `given a context when initialize method is invoked then the app name is extracted`() { 27 | val appName = "Gravatar APP" 28 | val packageName = "com.gravatar" 29 | val context = mockk() 30 | val packageManager = mockk() 31 | val applicationInfo = mockk() 32 | 33 | every { context.packageManager } returns packageManager 34 | every { context.packageName } returns packageName 35 | every { packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA) } returns applicationInfo 36 | every { packageManager.getApplicationLabel(applicationInfo) } returns appName 37 | 38 | Gravatar.context(context) 39 | 40 | coVerify(exactly = 1) { containerRule.gravatarSdkContainerMock.appName = appName } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /gravatar/src/test/java/com/gravatar/services/ServiceTestUtils.kt: -------------------------------------------------------------------------------- 1 | package com.gravatar.services 2 | 3 | import junit.framework.TestCase.assertEquals 4 | import junit.framework.TestCase.assertTrue 5 | import kotlinx.coroutines.test.runTest 6 | import kotlin.test.assertFailsWith 7 | 8 | internal fun runTestExpectingGravatarException( 9 | errorType: ErrorType, 10 | originalException: Class, 11 | block: suspend () -> Unit, 12 | ) { 13 | val exception = assertFailsWith { 14 | runTest { 15 | block() 16 | } 17 | } 18 | assertEquals(errorType, exception.errorType) 19 | assertTrue(originalException.isInstance(exception.originalException)) 20 | } 21 | -------------------------------------------------------------------------------- /lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("build-logic") 3 | repositories { 4 | google() 5 | mavenCentral() 6 | gradlePluginPortal() 7 | } 8 | plugins { 9 | id ("org.jetbrains.dokka") version ("1.9.20") 10 | } 11 | } 12 | dependencyResolutionManagement { 13 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 14 | repositories { 15 | google() 16 | mavenCentral() 17 | maven { 18 | url = uri("https://a8c-libs.s3.amazonaws.com/android") 19 | content { 20 | includeGroup("com.automattic") 21 | includeGroup("com.automattic.ucrop") 22 | } 23 | } 24 | } 25 | } 26 | 27 | rootProject.name = "gravatar" 28 | include(":gravatar") 29 | include(":demo-app") 30 | include(":gravatar-ui") 31 | include(":gravatar-quickeditor") 32 | include(":uitestutils") 33 | -------------------------------------------------------------------------------- /uitestutils/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /uitestutils/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Automattic/Gravatar-SDK-Android/9b24c03139de37a272adbd16e6d70a31445d5160/uitestutils/consumer-rules.pro -------------------------------------------------------------------------------- /uitestutils/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 -------------------------------------------------------------------------------- /uitestutils/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | --------------------------------------------------------------------------------