That fuck shit the fascists are using
1package org.tm.archive.util;
2
3import androidx.annotation.NonNull;
4import androidx.annotation.VisibleForTesting;
5
6import com.google.i18n.phonenumbers.NumberParseException;
7import com.google.i18n.phonenumbers.PhoneNumberUtil;
8
9import org.signal.core.util.logging.Log;
10import org.tm.archive.mms.PushMediaConstraints;
11import org.tm.archive.recipients.Recipient;
12
13import java.util.Arrays;
14import java.util.HashMap;
15import java.util.List;
16import java.util.Map;
17import java.util.Optional;
18import java.util.stream.Collectors;
19
20/**
21 * Provide access to locale specific values within feature flags following the locale CSV-Colon format.
22 *
23 * Example: countryCode:integerValue,countryCode:integerValue,*:integerValue
24 */
25public final class LocaleFeatureFlags {
26
27 private static final String TAG = Log.tag(LocaleFeatureFlags.class);
28
29 private static final String COUNTRY_WILDCARD = "*";
30 private static final int NOT_FOUND = -1;
31
32 public static @NonNull Optional<PushMediaConstraints.MediaConfig> getMediaQualityLevel() {
33 Map<String, Integer> countryValues = parseCountryValues(FeatureFlags.getMediaQualityLevels(), NOT_FOUND);
34 int level = getCountryValue(countryValues, Recipient.self().getE164().orElse(""), NOT_FOUND);
35
36 return Optional.ofNullable(PushMediaConstraints.MediaConfig.forLevel(level));
37 }
38
39 public static boolean shouldShowReleaseNote(@NonNull String releaseNoteUuid, @NonNull String countries) {
40 return isEnabledPartsPerMillion(releaseNoteUuid, countries);
41 }
42
43 /**
44 * @return Whether Google Pay is disabled in this region
45 */
46 public static boolean isGooglePayDisabled() {
47 return isEnabledE164Start(FeatureFlags.googlePayDisabledRegions());
48 }
49
50 /**
51 * @return Whether credit cards are disabled in this region
52 */
53 public static boolean isCreditCardDisabled() {
54 return isEnabledE164Start(FeatureFlags.creditCardDisabledRegions());
55 }
56
57 /**
58 * @return Whether PayPal is disabled in this region
59 */
60 public static boolean isPayPalDisabled() {
61 return isEnabledE164Start(FeatureFlags.paypalDisabledRegions());
62 }
63
64 public static boolean isIdealEnabled() {
65 return isEnabledE164Start(FeatureFlags.idealEnabledRegions());
66 }
67
68 public static boolean isSepaEnabled() {
69 return isEnabledE164Start(FeatureFlags.sepaEnabledRegions());
70 }
71
72 public static boolean isDelayedNotificationPromptEnabled() {
73 return FeatureFlags.internalUser() || isEnabledPartsPerMillion(FeatureFlags.PROMPT_FOR_NOTIFICATION_LOGS, FeatureFlags.promptForDelayedNotificationLogs());
74 }
75
76 public static boolean isBatterySaverPromptEnabled() {
77 return FeatureFlags.internalUser() || isEnabledPartsPerMillion(FeatureFlags.PROMPT_BATTERY_SAVER, FeatureFlags.promptBatterySaver());
78 }
79
80 /**
81 * Parses a comma-separated list of country codes and area codes to check if self's e164 starts with
82 * one of them. For example, "33,1555" will return turn for e164's that start with 33 or look like 1-555-xxx-xxx.
83 */
84 private static boolean isEnabledE164Start(@NonNull String serialized) {
85 Recipient self = Recipient.self();
86
87 if (self.getE164().isEmpty()) {
88 return false;
89 }
90
91 return isEnabledE164Start(serialized, self.getE164().get());
92 }
93
94 @VisibleForTesting
95 static boolean isEnabledE164Start(@NonNull String serialized, @NonNull String e164) {
96 List<String> countryAndAreaCodes = Arrays.stream(serialized.split(",")).map(s -> s.trim().replaceAll("\\s", "")).collect(Collectors.toList());
97 String e164Numbers = e164.replaceAll("\\D", "");
98
99 return countryAndAreaCodes.stream().anyMatch(e164Numbers::startsWith);
100 }
101
102 /**
103 * Parses a comma-separated list of country codes colon-separated from how many buckets out of 1 million
104 * should be enabled to see this megaphone in that country code. At the end of the list, an optional
105 * element saying how many buckets out of a million should be enabled for all countries not listed previously
106 * in the list. For example, "1:20000,*:40000" would mean 2% of the NANPA phone numbers and 4% of the rest of
107 * the world should see the megaphone.
108 */
109 private static boolean isEnabledPartsPerMillion(@NonNull String flag, @NonNull String serialized) {
110 Map<String, Integer> countryCodeValues = parseCountryValues(serialized, 0);
111 Recipient self = Recipient.self();
112
113 if (countryCodeValues.isEmpty() || !self.getE164().isPresent() || !self.getServiceId().isPresent()) {
114 return false;
115 }
116
117 long countEnabled = getCountryValue(countryCodeValues, self.getE164().orElse(""), 0);
118 long currentUserBucket = BucketingUtil.bucket(flag, self.requireAci().getRawUuid(), 1_000_000);
119
120 return countEnabled > currentUserBucket;
121 }
122
123 @VisibleForTesting
124 static @NonNull Map<String, Integer> parseCountryValues(@NonNull String buckets, int defaultValue) {
125 Map<String, Integer> countryCountEnabled = new HashMap<>();
126
127 for (String bucket : buckets.split(",")) {
128 String[] parts = bucket.split(":");
129 if (parts.length == 2 && !parts[0].isEmpty()) {
130 countryCountEnabled.put(parts[0], Util.parseInt(parts[1], defaultValue));
131 }
132 }
133 return countryCountEnabled;
134 }
135
136 @VisibleForTesting
137 static int getCountryValue(@NonNull Map<String, Integer> countryCodeValues, @NonNull String e164, int defaultValue) {
138 Integer countEnabled = countryCodeValues.get(COUNTRY_WILDCARD);
139 try {
140 String countryCode = String.valueOf(PhoneNumberUtil.getInstance().parse(e164, "").getCountryCode());
141 if (countryCodeValues.containsKey(countryCode)) {
142 countEnabled = countryCodeValues.get(countryCode);
143 }
144 } catch (NumberParseException e) {
145 Log.d(TAG, "Unable to determine country code for bucketing.");
146 return defaultValue;
147 }
148
149 return countEnabled != null ? countEnabled : defaultValue;
150 }
151}