mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-23 10:20:25 +01:00
Implement ability to view badges and modify whether they appear.
Note: this is available in staging only.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
package org.thoughtcrime.securesms.util
|
||||
|
||||
import org.thoughtcrime.securesms.BuildConfig
|
||||
|
||||
object Environment {
|
||||
const val IS_STAGING: Boolean = BuildConfig.BUILD_ENVIRONMENT_TYPE == "Staging"
|
||||
}
|
||||
@@ -84,6 +84,7 @@ public final class FeatureFlags {
|
||||
private static final String MAX_GROUP_CALL_RING_SIZE = "global.calling.maxGroupCallRingSize";
|
||||
private static final String GROUP_CALL_RINGING = "android.calling.groupCallRinging";
|
||||
private static final String CHANGE_NUMBER_ENABLED = "android.changeNumber";
|
||||
private static final String DONOR_BADGES = "android.donorBadges";
|
||||
|
||||
/**
|
||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||
@@ -126,7 +127,8 @@ public final class FeatureFlags {
|
||||
@VisibleForTesting
|
||||
static final Set<String> NOT_REMOTE_CAPABLE = SetUtil.newHashSet(
|
||||
PHONE_NUMBER_PRIVACY_VERSION,
|
||||
CHANGE_NUMBER_ENABLED
|
||||
CHANGE_NUMBER_ENABLED,
|
||||
DONOR_BADGES
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -394,11 +396,20 @@ public final class FeatureFlags {
|
||||
return getBoolean(GROUP_CALL_RINGING, false);
|
||||
}
|
||||
|
||||
/** Weather or not to show change number in the UI. */
|
||||
/** Whether or not to show change number in the UI. */
|
||||
public static boolean changeNumber() {
|
||||
return getBoolean(CHANGE_NUMBER_ENABLED, false);
|
||||
}
|
||||
|
||||
/** Whether or not to show donor badges in the UI. */
|
||||
public static boolean donorBadges() {
|
||||
if (Environment.IS_STAGING) {
|
||||
return true;
|
||||
} else {
|
||||
return getBoolean(DONOR_BADGES, false);
|
||||
}
|
||||
}
|
||||
|
||||
/** Only for rendering debug info. */
|
||||
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
||||
return new TreeMap<>(REMOTE_VALUES);
|
||||
|
||||
@@ -9,6 +9,7 @@ import androidx.annotation.WorkerThread;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.zkgroup.InvalidInputException;
|
||||
import org.signal.zkgroup.profiles.ProfileKey;
|
||||
import org.thoughtcrime.securesms.badges.models.Badge;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
@@ -42,6 +43,8 @@ import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
|
||||
@@ -60,12 +63,22 @@ public final class ProfileUtil {
|
||||
@NonNull Recipient recipient,
|
||||
@NonNull SignalServiceProfile.RequestType requestType)
|
||||
throws IOException
|
||||
{
|
||||
return retrieveProfileSync(context, recipient, requestType, true);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static @NonNull ProfileAndCredential retrieveProfileSync(@NonNull Context context,
|
||||
@NonNull Recipient recipient,
|
||||
@NonNull SignalServiceProfile.RequestType requestType,
|
||||
boolean allowUnidentifiedAccess)
|
||||
throws IOException
|
||||
{
|
||||
ProfileService profileService = new ProfileService(ApplicationDependencies.getGroupsV2Operations().getProfileOperations(),
|
||||
ApplicationDependencies.getSignalServiceMessageReceiver(),
|
||||
ApplicationDependencies.getSignalWebSocket());
|
||||
|
||||
Pair<Recipient, ServiceResponse<ProfileAndCredential>> response = retrieveProfile(context, recipient, requestType, profileService).blockingGet();
|
||||
Pair<Recipient, ServiceResponse<ProfileAndCredential>> response = retrieveProfile(context, recipient, requestType, profileService, allowUnidentifiedAccess).blockingGet();
|
||||
return new ProfileService.ProfileResponseProcessor(response.second()).getResultOrThrow();
|
||||
}
|
||||
|
||||
@@ -74,7 +87,16 @@ public final class ProfileUtil {
|
||||
@NonNull SignalServiceProfile.RequestType requestType,
|
||||
@NonNull ProfileService profileService)
|
||||
{
|
||||
Optional<UnidentifiedAccess> unidentifiedAccess = getUnidentifiedAccess(context, recipient);
|
||||
return retrieveProfile(context, recipient, requestType, profileService, true);
|
||||
}
|
||||
|
||||
private static Single<Pair<Recipient, ServiceResponse<ProfileAndCredential>>> retrieveProfile(@NonNull Context context,
|
||||
@NonNull Recipient recipient,
|
||||
@NonNull SignalServiceProfile.RequestType requestType,
|
||||
@NonNull ProfileService profileService,
|
||||
boolean allowUnidentifiedAccess)
|
||||
{
|
||||
Optional<UnidentifiedAccess> unidentifiedAccess = allowUnidentifiedAccess ? getUnidentifiedAccess(context, recipient) : Optional.absent();
|
||||
Optional<ProfileKey> profileKey = ProfileKeyUtil.profileKeyOptional(recipient.getProfileKey());
|
||||
|
||||
return Single.fromCallable(() -> toSignalServiceAddress(context, recipient))
|
||||
@@ -163,6 +185,23 @@ public final class ProfileUtil {
|
||||
return profileKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the profile based on all state that's written to disk, except we'll use the provided
|
||||
* list of badges instead. This is useful when you want to ensure that the profile has been uploaded
|
||||
* successfully before persisting the change to disk.
|
||||
*/
|
||||
public static void uploadProfileWithBadges(@NonNull Context context, @NonNull List<Badge> badges) throws IOException {
|
||||
try (StreamDetails avatar = AvatarHelper.getSelfProfileAvatarStream(context)) {
|
||||
uploadProfile(context,
|
||||
Recipient.self().getProfileName(),
|
||||
Optional.fromNullable(Recipient.self().getAbout()).or(""),
|
||||
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""),
|
||||
getSelfPaymentsAddressProtobuf(),
|
||||
avatar,
|
||||
badges);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the profile based on all state that's written to disk, except we'll use the provided
|
||||
* profile name instead. This is useful when you want to ensure that the profile has been uploaded
|
||||
@@ -175,7 +214,8 @@ public final class ProfileUtil {
|
||||
Optional.fromNullable(Recipient.self().getAbout()).or(""),
|
||||
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""),
|
||||
getSelfPaymentsAddressProtobuf(),
|
||||
avatar);
|
||||
avatar,
|
||||
Recipient.self().getBadges());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +231,8 @@ public final class ProfileUtil {
|
||||
about,
|
||||
emoji,
|
||||
getSelfPaymentsAddressProtobuf(),
|
||||
avatar);
|
||||
avatar,
|
||||
Recipient.self().getBadges());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +256,8 @@ public final class ProfileUtil {
|
||||
Optional.fromNullable(Recipient.self().getAbout()).or(""),
|
||||
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""),
|
||||
getSelfPaymentsAddressProtobuf(),
|
||||
avatar);
|
||||
avatar,
|
||||
Recipient.self().getBadges());
|
||||
}
|
||||
|
||||
private static void uploadProfile(@NonNull Context context,
|
||||
@@ -223,13 +265,21 @@ public final class ProfileUtil {
|
||||
@Nullable String about,
|
||||
@Nullable String aboutEmoji,
|
||||
@Nullable SignalServiceProtos.PaymentAddress paymentsAddress,
|
||||
@Nullable StreamDetails avatar)
|
||||
@Nullable StreamDetails avatar,
|
||||
@NonNull List<Badge> badges)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
List<String> badgeIds = badges.stream()
|
||||
.filter(Badge::getVisible)
|
||||
.map(Badge::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Log.d(TAG, "Uploading " + (!Util.isEmpty(about) ? "non-" : "") + "empty about.");
|
||||
Log.d(TAG, "Uploading " + (!Util.isEmpty(aboutEmoji) ? "non-" : "") + "empty emoji.");
|
||||
Log.d(TAG, "Uploading " + (paymentsAddress != null ? "non-" : "") + "empty payments address.");
|
||||
Log.d(TAG, "Uploading " + (avatar != null && avatar.getLength() != 0 ? "non-" : "") + "empty avatar.");
|
||||
Log.d(TAG, "Uploading " + ((!badgeIds.isEmpty()) ? "non-" : "") + "empty badge list");
|
||||
|
||||
ProfileKey profileKey = ProfileKeyUtil.getSelfProfileKey();
|
||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||
@@ -239,7 +289,8 @@ public final class ProfileUtil {
|
||||
about,
|
||||
aboutEmoji,
|
||||
Optional.fromNullable(paymentsAddress),
|
||||
avatar).orNull();
|
||||
avatar,
|
||||
badgeIds).orNull();
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileAvatar(Recipient.self().getId(), avatarPath);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user