├── .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 |
7 |
8 |
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 |
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 |
7 |
8 |
9 |
10 | (bundled)
11 | (bundled)
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/gravatar/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/gravatar/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/gravatar/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
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 |
--------------------------------------------------------------------------------