Skip to content

Commit

Permalink
Merge branch 'trunk' into issue/21465-remove-reader-feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
nbradbury authored Nov 15, 2024
2 parents 61bd381 + 24699b0 commit bf6377a
Show file tree
Hide file tree
Showing 16 changed files with 132 additions and 42 deletions.
4 changes: 3 additions & 1 deletion WordPress/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,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 @@ -406,6 +407,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 @@ -454,7 +456,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 @@ -95,7 +95,6 @@
import org.wordpress.android.ui.posts.PostsListActivity;
import org.wordpress.android.ui.posts.RemotePreviewLogicHelper.RemotePreviewType;
import org.wordpress.android.ui.prefs.AccountSettingsActivity;
import org.wordpress.android.ui.prefs.AppPrefs;
import org.wordpress.android.ui.prefs.AppSettingsActivity;
import org.wordpress.android.ui.prefs.BlogPreferencesActivity;
import org.wordpress.android.ui.prefs.MyProfileActivity;
Expand Down Expand Up @@ -128,7 +127,6 @@
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;
import org.wordpress.android.util.ToastUtils;
import org.wordpress.android.util.ToastUtils.Duration;
import org.wordpress.android.util.UriWrapper;
import org.wordpress.android.util.UrlUtils;
import org.wordpress.android.util.WPActivityUtils;
Expand Down Expand Up @@ -1313,7 +1311,6 @@ public static void viewHelp(@NonNull Context context, @NonNull Origin origin, @N
}

public static void viewFeedbackForm(@NonNull Context context) {
warnIfIdentityA8C(context);
AnalyticsTracker.track(Stat.APP_REVIEWS_FEEDBACK_SCREEN_OPENED);
Intent intent = new Intent(context, FeedbackFormActivity.class);
context.startActivity(intent);
Expand All @@ -1324,17 +1321,6 @@ public static void viewZendeskTickets(@NonNull Context context,
viewHelpInNewStack(context, Origin.ZENDESK_NOTIFICATION, selectedSite, null);
}

/**
* Warn A8C users that they can't create Zendesk tickets
*/
@NonNull
public static void warnIfIdentityA8C(@NonNull Context context) {
String supportEmail = AppPrefs.getSupportEmail();
if (supportEmail.contains("@automattic.com") || supportEmail.contains("@a8c.com")) {
ToastUtils.showToast(context, R.string.support_warn_if_user_a8c, Duration.LONG);
}
}

public static void viewSSLCerts(Context context, String certificateString) {
Intent intent = new Intent(context, SSLCertsViewActivity.class);
intent.putExtra(SSLCertsViewActivity.CERT_DETAILS_KEYS, certificateString.replaceAll("\n", "<br/>"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ class HelpActivity : LocaleAwareActivity() {
}

private fun launchSupportWidget() {
ActivityLauncher.warnIfIdentityA8C(this)
val intent = SupportWebViewActivity.createIntent(
this,
originFromExtras,
Expand All @@ -184,7 +183,6 @@ class HelpActivity : LocaleAwareActivity() {
}

private fun createNewZendeskTicket() {
ActivityLauncher.warnIfIdentityA8C(this)
zendeskHelper.createNewTicket(
this,
originFromExtras,
Expand All @@ -199,7 +197,6 @@ class HelpActivity : LocaleAwareActivity() {
}

private fun showZendeskTickets() {
ActivityLauncher.warnIfIdentityA8C(this)
zendeskHelper.showAllTickets(
this,
originFromExtras,
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 @@ -22,6 +22,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.lifecycle.compose.LocalLifecycleOwner
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 @@ -470,11 +493,12 @@ 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 newAvatarSelected = newAvatarUploaded || forceRefresh
meGravatarLoader.load(
newAvatarUploaded,
newAvatarSelected,
avatarUrl,
injectFilePath,
meAvatar,
Expand Down Expand Up @@ -508,7 +532,7 @@ class MeFragment : Fragment(R.layout.me_fragment), OnScrollToTopListener {
resource: Drawable,
model: Any?
) {
if (newAvatarUploaded && resource is BitmapDrawable) {
if (newAvatarSelected && resource is BitmapDrawable) {
var bitmap = resource.bitmap
// create a copy since the original bitmap may by automatically recycled
bitmap = bitmap.copy(bitmap.config, true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ class MeGravatarLoader @Inject constructor(
private val resourseProvider: ResourceProvider
) {
fun load(
newAvatarUploaded: Boolean,
newAvatarSelected: Boolean,
avatarUrl: String,
injectFilePath: String?,
imageView: ImageView,
imageType: ImageType,
listener: RequestListener<Drawable>? = null
) {
if (newAvatarUploaded) {
if (newAvatarSelected) {
// invalidate the specific gravatar entry from the bitmap cache. It will be updated via the injected
// request cache.
WordPress.getBitmapCache().removeSimilar(avatarUrl)
Expand All @@ -45,19 +45,22 @@ class MeGravatarLoader @Inject constructor(
imageManager.loadIntoCircle(
imageView,
imageType,
if (newAvatarUploaded && injectFilePath != null) {
if (newAvatarSelected && injectFilePath != null) {
injectFilePath
} else {
avatarUrl
// If new avatar selected we force refresh the avatar
val constructGravatarUrl = constructGravatarUrl(avatarUrl, newAvatarSelected)
constructGravatarUrl
},
listener,
appPrefsWrapper.avatarVersion
)
}
}

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 @@ -894,7 +894,15 @@ public static void setSupportEmail(String email) {
}

public static String getSupportEmail() {
return getString(DeletablePrefKey.SUPPORT_EMAIL);
String email = getString(DeletablePrefKey.SUPPORT_EMAIL);
// Zendesk can't create support tickets for Automattic email addresses
// with a staff member role (admin, agent, etc.), so insert an "+testing"
// alias into the email to make it work
if ((email.contains("@automattic.com") || email.contains("@a8c.com")) && !email.contains("+")) {
return email.replace("@", "+testing@");
} else {
return email;
}
}

public static void removeSupportEmail() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ private fun ExperimentalBadge(
)
}


private fun getTitleTextStyle(
fontFamily: FontFamily,
fontSizeMultiplier: Float,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ private fun Loaded(uiState: UiState.Loaded) {

Column(
modifier = Modifier
.animateItem(fadeInSpec = null, fadeOutSpec = null)
.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"
}
}
Loading

0 comments on commit bf6377a

Please sign in to comment.