Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion WordPress/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ repositories {
includeGroupByRegex "org.wordpress.react-native-libraries.*"
includeGroup "com.automattic"
includeGroup "com.automattic.tracks"
includeGroup "com.automattic.ucrop"
includeGroup "com.gravatar"
includeGroup "rs.wordpress.api"
}
Expand Down Expand Up @@ -202,6 +203,7 @@ android {
buildConfigField "boolean", "VOICE_TO_CONTENT", "false"
buildConfigField "boolean", "READER_FLOATING_BUTTON", "false"
buildConfigField "boolean", "ENABLE_SELF_HOSTED_USERS", "false"
buildConfigField "boolean", "GRAVATAR_QUICK_EDITOR", "true"

// Override these constants in jetpack product flavor to enable/ disable features
buildConfigField "boolean", "ENABLE_SITE_CREATION", "true"
Expand Down Expand Up @@ -430,6 +432,7 @@ dependencies {
}
implementation(libs.wordpress.persistent.edittext)
implementation("$gradle.ext.gravatarBinaryPath:${libs.versions.gravatar.get()}")
implementation("$gradle.ext.gravatarQuickEditorBinaryPath:${libs.versions.gravatar.get()}")

implementation(libs.google.play.app.update)

Expand Down Expand Up @@ -478,7 +481,7 @@ dependencies {
implementation(libs.apache.commons.text)
implementation(libs.airbnb.lottie.main)
implementation(libs.facebook.shimmer)
implementation(libs.yalantis.ucrop) {
implementation(libs.automattic.ucrop) {
exclude group: 'androidx.core', module: 'core'
exclude group: 'androidx.constraintlayout', module: 'constraintlayout'
exclude group: 'androidx.appcompat', module: 'appcompat'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.gravatar.AvatarQueryOptions
import com.gravatar.AvatarUrl
import com.gravatar.quickeditor.GravatarQuickEditor
import com.gravatar.quickeditor.ui.editor.AuthenticationMethod
import com.gravatar.quickeditor.ui.editor.AvatarPickerContentLayout
import com.gravatar.quickeditor.ui.editor.GravatarQuickEditorParams
import com.gravatar.services.AvatarService
import com.gravatar.services.GravatarResult
import com.gravatar.types.Email
Expand Down Expand Up @@ -70,6 +76,7 @@ import org.wordpress.android.util.StringUtils
import org.wordpress.android.util.ToastUtils
import org.wordpress.android.util.WPAvatarUtils
import org.wordpress.android.util.WPMediaUtils
import org.wordpress.android.util.config.GravatarQuickEditorFeatureConfig
import org.wordpress.android.util.extensions.getColorFromAttribute
import org.wordpress.android.util.extensions.redirectContextClickToLongPressListener
import org.wordpress.android.util.image.ImageManager
Expand Down Expand Up @@ -134,6 +141,9 @@ class SignupEpilogueFragment : LoginBaseFormFragment<SignupEpilogueListener?>(),
@Inject
lateinit var mAvatarService: AvatarService

@Inject
lateinit var gravatarQuickEditorFeatureConfig: GravatarQuickEditorFeatureConfig

@LayoutRes
override fun getContentLayout(): Int {
return 0 // no content layout; entire view is inflated in createMainView
Expand Down Expand Up @@ -163,7 +173,33 @@ class SignupEpilogueFragment : LoginBaseFormFragment<SignupEpilogueListener?>(),
headerAvatarLayout.isEnabled = mIsEmailSignup
headerAvatarLayout.setOnClickListener {
mUnifiedLoginTracker.trackClick(UnifiedLoginTracker.Click.SELECT_AVATAR)
mMediaPickerLauncher.showGravatarPicker(this@SignupEpilogueFragment)
if (gravatarQuickEditorFeatureConfig.isEnabled()) {
GravatarQuickEditor.show(
fragment = this,
gravatarQuickEditorParams = GravatarQuickEditorParams {
email = Email(mEmailAddress)
avatarPickerContentLayout = AvatarPickerContentLayout.Horizontal
},
authenticationMethod = AuthenticationMethod.Bearer(mAccount.accessToken.orEmpty()),
onAvatarSelected = {
mPhotoUrl = AvatarUrl(
email = Email(mEmailAddress),
avatarQueryOptions = AvatarQueryOptions {
preferredSize = resources.getDimensionPixelSize(R.dimen.avatar_sz_large)
}
).url(cacheBuster = System.currentTimeMillis().toString()).toString()
mImageManager.loadIntoCircle(
mHeaderAvatar,
ImageType.AVATAR_WITHOUT_BACKGROUND,
mPhotoUrl
)
mHeaderAvatarAdd.visibility = View.GONE
mIsAvatarAdded = true
},
)
} else {
mMediaPickerLauncher.showGravatarPicker(this@SignupEpilogueFragment)
}
}
headerAvatarLayout.setOnLongClickListener {
ToastUtils.showToast(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import org.wordpress.android.ui.compose.theme.AppThemeM2
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.camera.core.Preview as CameraPreview

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.gravatar.quickeditor.GravatarQuickEditor
import com.gravatar.quickeditor.ui.editor.AuthenticationMethod
import com.gravatar.quickeditor.ui.editor.AvatarPickerContentLayout
import com.gravatar.quickeditor.ui.editor.GravatarQuickEditorParams
import com.gravatar.services.AvatarService
import com.gravatar.services.GravatarResult
import com.gravatar.types.Email
Expand Down Expand Up @@ -82,6 +86,7 @@ import org.wordpress.android.util.ToastUtils
import org.wordpress.android.util.ToastUtils.Duration.SHORT
import org.wordpress.android.util.WPMediaUtils
import org.wordpress.android.util.config.DomainManagementFeatureConfig
import org.wordpress.android.util.config.GravatarQuickEditorFeatureConfig
import org.wordpress.android.util.config.QRCodeAuthFlowFeatureConfig
import org.wordpress.android.util.config.RecommendTheAppFeatureConfig
import org.wordpress.android.util.extensions.getColorFromAttribute
Expand Down Expand Up @@ -131,6 +136,9 @@ class MeFragment : Fragment(R.layout.me_fragment), OnScrollToTopListener {
@Inject
lateinit var qrCodeAuthFlowFeatureConfig: QRCodeAuthFlowFeatureConfig

@Inject
lateinit var gravatarQuickEditorFeatureConfig: GravatarQuickEditorFeatureConfig

@Inject
lateinit var jetpackBrandingUtils: JetpackBrandingUtils

Expand All @@ -156,6 +164,7 @@ class MeFragment : Fragment(R.layout.me_fragment), OnScrollToTopListener {

private val shouldShowDomainButton
get() = BuildConfig.IS_JETPACK_APP && domainManagementFeatureConfig.isEnabled() && accountStore.hasAccessToken()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(requireActivity().application as WordPress).component().inject(this)
Expand Down Expand Up @@ -192,7 +201,21 @@ class MeFragment : Fragment(R.layout.me_fragment), OnScrollToTopListener {

val showPickerListener = OnClickListener {
AnalyticsTracker.track(ME_GRAVATAR_TAPPED)
showPhotoPickerForGravatar()
if (gravatarQuickEditorFeatureConfig.isEnabled()) {
GravatarQuickEditor.show(
fragment = this@MeFragment,
gravatarQuickEditorParams = GravatarQuickEditorParams {
email = Email(accountStore.account.email)
avatarPickerContentLayout = AvatarPickerContentLayout.Horizontal
},
authenticationMethod = AuthenticationMethod.Bearer(accountStore.accessToken.orEmpty()),
onAvatarSelected = {
loadAvatar(null, true)
},
)
} else {
showPhotoPickerForGravatar()
}
}
avatarContainer.setOnClickListener(showPickerListener)
rowMyProfile.setOnClickListener {
Expand Down Expand Up @@ -473,9 +496,9 @@ class MeFragment : Fragment(R.layout.me_fragment), OnScrollToTopListener {
isUpdatingGravatar = isUpdating
}

private fun MeFragmentBinding.loadAvatar(injectFilePath: String?) {
private fun MeFragmentBinding.loadAvatar(injectFilePath: String?, forceRefresh: Boolean = false) {
val newAvatarUploaded = !injectFilePath.isNullOrEmpty()
val avatarUrl = meGravatarLoader.constructGravatarUrl(accountStore.account.avatarUrl)
val avatarUrl = meGravatarLoader.constructGravatarUrl(accountStore.account.avatarUrl, forceRefresh)
meGravatarLoader.load(
newAvatarUploaded,
avatarUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ class MeGravatarLoader @Inject constructor(
}
}

fun constructGravatarUrl(rawAvatarUrl: String): String {
fun constructGravatarUrl(rawAvatarUrl: String, forceRefresh: Boolean = false): String {
val avatarSz = resourseProvider.getDimensionPixelSize(R.dimen.avatar_sz_extra_small)
return WPAvatarUtils.rewriteAvatarUrl(rawAvatarUrl, avatarSz)
val cacheBuster = if (forceRefresh) System.currentTimeMillis().toString() else null
return WPAvatarUtils.rewriteAvatarUrl(rawAvatarUrl, avatarSz, cacheBuster)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
Expand All @@ -37,6 +36,7 @@ import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
Expand Down Expand Up @@ -283,27 +283,22 @@ private fun ReadingPreferencesPreviewFeedback(
end = endIndex,
)

addStringAnnotation(
tag = "url",
annotation = "feedback",
addLink(
clickable = LinkAnnotation.Clickable(
tag = "url",
linkInteractionListener = {
onSendFeedbackClick()
}
),
start = startIndex,
end = endIndex,
)
}

val buttonLabel = stringResource(R.string.reader_preferences_screen_preview_text_feedback_label)
ClickableText(
Text(
text = annotatedString,
style = textStyle,
onClick = { offset ->
annotatedString.getStringAnnotations(tag = "url", start = offset, end = offset)
.firstOrNull()
?.let { annotation ->
if (annotation.item == "feedback") {
onSendFeedbackClick()
}
}
},
modifier = Modifier.semantics {
onClick(
label = buttonLabel,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.wordpress.android.ui.reader.views.compose.tagsfeed

import android.content.res.Configuration
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
Expand Down Expand Up @@ -91,7 +90,7 @@ fun ReaderTagsFeed(uiState: UiState) {
}
}

@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun Loaded(uiState: UiState.Loaded) {
val pullRefreshState = rememberPullRefreshState(
Expand Down Expand Up @@ -138,7 +137,7 @@ private fun Loaded(uiState: UiState.Loaded) {

Column(
modifier = Modifier
.animateItemPlacement()
.animateItem()
.fillMaxWidth()
.padding(
top = Margin.Large.value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class WPAvatarUtils {
private WPAvatarUtils() {
throw new IllegalStateException("Utility class");
}

public static final DefaultAvatarOption DEFAULT_AVATAR = MysteryPerson.INSTANCE;

/**
Expand All @@ -35,7 +36,8 @@ private WPAvatarUtils() {
* @return the fixed url
*/
public static String rewriteAvatarUrl(@NonNull final String imageUrl, int avatarSz,
@Nullable DefaultAvatarOption defaultImage) {
@Nullable DefaultAvatarOption defaultImage,
@Nullable String cacheBuster) {
if (TextUtils.isEmpty(imageUrl)) {
return "";
}
Expand All @@ -47,17 +49,27 @@ public static String rewriteAvatarUrl(@NonNull final String imageUrl, int avatar
try {
return new AvatarUrl(new URL(imageUrl),
new AvatarQueryOptions.Builder()
.setPreferredSize(avatarSz)
.setDefaultAvatarOption(defaultImage)
.build()
).url(null).toString();
.setPreferredSize(avatarSz)
.setDefaultAvatarOption(defaultImage)
.build()
).url(cacheBuster).toString();
} catch (MalformedURLException | IllegalArgumentException e) {
return "";
}
}
}

public static String rewriteAvatarUrl(@NonNull final String imageUrl, int avatarSz) {
return rewriteAvatarUrl(imageUrl, avatarSz, DEFAULT_AVATAR);
return rewriteAvatarUrl(imageUrl, avatarSz, DEFAULT_AVATAR, null);
}

public static String rewriteAvatarUrl(@NonNull final String imageUrl, int avatarSz,
@Nullable DefaultAvatarOption defaultImage) {
return rewriteAvatarUrl(imageUrl, avatarSz, defaultImage, null);
}

public static String rewriteAvatarUrl(@NonNull final String imageUrl, int avatarSz,
@Nullable String cacheBuster) {
return rewriteAvatarUrl(imageUrl, avatarSz, DEFAULT_AVATAR, cacheBuster);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.wordpress.android.util.config

import org.wordpress.android.BuildConfig
import org.wordpress.android.annotation.Feature
import javax.inject.Inject

@Feature(GravatarQuickEditorFeatureConfig.GRAVATAR_QUICK_EDITOR_REMOTE_FIELD, true)
class GravatarQuickEditorFeatureConfig @Inject constructor(appConfig: AppConfig) : FeatureConfig(
appConfig,
BuildConfig.GRAVATAR_QUICK_EDITOR,
GRAVATAR_QUICK_EDITOR_REMOTE_FIELD
) {
override fun isEnabled(): Boolean {
return super.isEnabled() && BuildConfig.GRAVATAR_QUICK_EDITOR
}

companion object {
const val GRAVATAR_QUICK_EDITOR_REMOTE_FIELD = "gravatar_quick_editor"
}
}
1 change: 1 addition & 0 deletions config/gradle/included_builds.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ gradle.ext.aztecAndroidGlideLoaderPath = "org.wordpress.aztec:glide-loader"
gradle.ext.aztecAndroidPicassoLoaderPath = "org.wordpress.aztec:picasso-loader"
gradle.ext.aboutAutomatticBinaryPath = "com.automattic:about"
gradle.ext.gravatarBinaryPath = "com.gravatar:gravatar"
gradle.ext.gravatarQuickEditorBinaryPath = "com.gravatar:gravatar-quickeditor"

def localBuilds = new File("${rootDir}/local-builds.gradle")
if (localBuilds.exists()) {
Expand Down
6 changes: 3 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ androidx-constraintlayout-compose = '1.0.1'
androidx-core = '1.13.1'
androidx-fragment = '1.8.4'
androidx-lifecycle = '2.8.5'
androidx-navigation = '2.7.7'
androidx-navigation = '2.8.3'
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bug in 2.8.1 (used by QuickEditor) that causes some Lint warnings. WP is configured to fail on those so I had to bump the library here to fix the issue.

Here's the issue tracker with a bit more info - https://issuetracker.google.com/issues/368070326

We will update the library in the Gravatar SDK as well, but for now, this should do the trick.

@nbradbury Any thoughts on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally I'd opt for updating a library in a dedicated PR, but I just tested the app separately with androidx-navigation v2.8.3 and ran into no problems, so I'm fine with updating it here.

androidx-percentlayout = '1.0.0'
androidx-preference = '1.2.1'
androidx-recyclerview = '1.3.2'
Expand Down Expand Up @@ -92,7 +92,7 @@ wordpress-lint = '2.1.0'
wordpress-persistent-edittext = '1.0.2'
wordpress-rs = 'trunk-50f703a7f677084157d02f05d4d477d7eaf960b1'
wordpress-utils = '3.14.0'
yalantis-ucrop = '2.2.9'
automattic-ucrop = '2.2.10'
zendesk = '5.1.2'

[libraries]
Expand Down Expand Up @@ -225,7 +225,7 @@ wordpress-lint = { group = "org.wordpress", name = "lint", version.ref = "wordpr
wordpress-persistent-edittext = { group = "org.wordpress", name = "persistentedittext", version.ref = "wordpress-persistent-edittext" }
wordpress-rs-android = { group = "rs.wordpress.api", name = "android", version.ref = "wordpress-rs" }
wordpress-utils = { group = "org.wordpress", name = "utils", version.ref = "wordpress-utils" }
yalantis-ucrop = { group = "com.github.yalantis", name = "ucrop", version.ref = "yalantis-ucrop" }
automattic-ucrop = { group = "com.automattic", name = "ucrop", version.ref = "automattic-ucrop" }
zendesk-support = { group = "com.zendesk", name = "support", version.ref = "zendesk" }

[plugins]
Expand Down
Loading