diff --git a/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiSource.kt b/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiSource.kt index ebf77c593..e4dd45487 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiSource.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiSource.kt @@ -2,19 +2,18 @@ package org.thoughtcrime.securesms.emoji import android.net.Uri import androidx.annotation.WorkerThread -import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.emoji.Emoji import org.thoughtcrime.securesms.components.emoji.EmojiPageModel import org.thoughtcrime.securesms.components.emoji.StaticEmojiPageModel import org.thoughtcrime.securesms.components.emoji.parsing.EmojiDrawInfo import org.thoughtcrime.securesms.components.emoji.parsing.EmojiTree import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader import org.thoughtcrime.securesms.util.ScreenDensity import java.io.InputStream import java.util.concurrent.CountDownLatch import java.util.concurrent.atomic.AtomicReference -import kotlin.math.min /** * The entry point for the application to request Emoji data for custom emojis. @@ -88,27 +87,27 @@ class EmojiSource( } private fun loadRemoteBasedEmojis(): EmojiSource? { + if (SignalStore.internalValues().forceBuiltInEmoji()) { + return null + } + val context = ApplicationDependencies.getApplication() val version = EmojiFiles.Version.readVersion(context) ?: return null val emojiData = EmojiFiles.getLatestEmojiData(context, version) val density = ScreenDensity.xhdpiRelativeDensityScaleFactor(version.density) return emojiData?.let { - val decodeScale = min(1f, context.resources.getDimension(R.dimen.emoji_drawer_size) / it.metrics.rawHeight) - - EmojiSource(decodeScale * density, it) { uri: Uri -> EmojiPageReference(DecryptableStreamUriLoader.DecryptableUri(uri)) } + EmojiSource(density, it) { uri: Uri -> EmojiPageReference(DecryptableStreamUriLoader.DecryptableUri(uri)) } } } private fun loadAssetBasedEmojis(): EmojiSource { - val context = ApplicationDependencies.getApplication() val emojiData: InputStream = ApplicationDependencies.getApplication().assets.open("emoji/emoji_data.json") emojiData.use { val parsedData: ParsedEmojiData = EmojiJsonParser.parse(it, ::getAssetsUri).getOrThrow() - val decodeScale = min(1f, context.resources.getDimension(R.dimen.emoji_drawer_size) / parsedData.metrics.rawHeight) return EmojiSource( - decodeScale * ScreenDensity.xhdpiRelativeDensityScaleFactor("xhdpi"), + ScreenDensity.xhdpiRelativeDensityScaleFactor("xhdpi"), parsedData.copy( displayPages = parsedData.displayPages + PAGE_EMOTICONS, dataPages = parsedData.dataPages + PAGE_EMOTICONS diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java index 7e312c1af..79a18ee0b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java @@ -17,6 +17,7 @@ public final class InternalValues extends SignalStoreValues { public static final String GV2_DISABLE_AUTOMIGRATE_NOTIFICATION = "internal.gv2.disable_automigrate_notification"; public static final String RECIPIENT_DETAILS = "internal.recipient_details"; public static final String FORCE_CENSORSHIP = "internal.force_censorship"; + public static final String FORCE_BUILT_IN_EMOJI = "internal.force_built_in_emoji"; InternalValues(KeyValueStore store) { super(store); @@ -80,6 +81,13 @@ public final class InternalValues extends SignalStoreValues { return FeatureFlags.internalUser() && getBoolean(FORCE_CENSORSHIP, false); } + /** + * Force the app to behave as if it is in a country where Signal is censored. + */ + public synchronized boolean forceBuiltInEmoji() { + return FeatureFlags.internalUser() && getBoolean(FORCE_BUILT_IN_EMOJI, false); + } + /** * Disable initiating a GV1->GV2 auto-migration. You can still recognize a group has been * auto-migrated. diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSystemInfo.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSystemInfo.java index 9d329b8bc..a1a0b3310 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSystemInfo.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSystemInfo.java @@ -14,6 +14,8 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import org.thoughtcrime.securesms.BuildConfig; +import org.thoughtcrime.securesms.emoji.EmojiFiles; +import org.thoughtcrime.securesms.emoji.EmojiSource; import org.thoughtcrime.securesms.util.AppSignatureUtil; import org.thoughtcrime.securesms.util.ByteUnit; import org.thoughtcrime.securesms.util.CensorshipUtil; @@ -66,6 +68,7 @@ public class LogSectionSystemInfo implements LogSection { builder.append("Linked Devices: ").append(TextSecurePreferences.isMultiDevice(context)).append("\n"); builder.append("First Version : ").append(TextSecurePreferences.getFirstInstallVersion(context)).append("\n"); builder.append("Days Installed: ").append(VersionTracker.getDaysSinceFirstInstalled(context)).append("\n"); + builder.append("Emoji Version : ").append(getEmojiVersionString(context)).append("\n"); builder.append("App : "); try { builder.append(pm.getApplicationLabel(pm.getApplicationInfo(context.getPackageName(), 0))) @@ -146,4 +149,14 @@ public class LogSectionSystemInfo implements LogSection { int result = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context); return result == ConnectionResult.SUCCESS ? "true" : "false (" + result + ")"; } + + private static String getEmojiVersionString(@NonNull Context context) { + EmojiFiles.Version version = EmojiFiles.Version.readVersion(context); + + if (version == null) { + return "None"; + } else { + return version.getVersion() + " (" + version.getDensity() + ")"; + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/InternalOptionsPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/InternalOptionsPreferenceFragment.java index 7da62d2c7..6bd8fa931 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/InternalOptionsPreferenceFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/InternalOptionsPreferenceFragment.java @@ -16,6 +16,8 @@ import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.emoji.EmojiFiles; +import org.thoughtcrime.securesms.emoji.EmojiSource; import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob; import org.thoughtcrime.securesms.jobs.RemoteConfigRefreshJob; @@ -49,6 +51,7 @@ public class InternalOptionsPreferenceFragment extends CorrectedPreferenceFragme initializeSwitchPreference(preferenceDataStore, InternalValues.GV2_DISABLE_AUTOMIGRATE_INITIATION, SignalStore.internalValues().disableGv1AutoMigrateInitiation()); initializeSwitchPreference(preferenceDataStore, InternalValues.GV2_DISABLE_AUTOMIGRATE_NOTIFICATION, SignalStore.internalValues().disableGv1AutoMigrateNotification()); initializeSwitchPreference(preferenceDataStore, InternalValues.FORCE_CENSORSHIP, SignalStore.internalValues().forcedCensorship()); + initializeSwitchPreference(preferenceDataStore, InternalValues.FORCE_BUILT_IN_EMOJI, SignalStore.internalValues().forceBuiltInEmoji()); findPreference("pref_copy_payments_data").setOnPreferenceClickListener(preference -> { new AlertDialog.Builder(getContext()) @@ -122,5 +125,21 @@ public class InternalOptionsPreferenceFragment extends CorrectedPreferenceFragme super.onResume(); //noinspection ConstantConditions ((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.preferences__internal_preferences); + + SimpleTask.run(getViewLifecycleOwner().getLifecycle(), + () -> EmojiFiles.Version.readVersion(requireContext()), + version -> { + if (version != null) { + findPreference(InternalValues.FORCE_BUILT_IN_EMOJI).setSummary(getString(R.string.preferences__internal_current_version_d_at_density_s, version.getVersion(), version.getDensity())); + } else { + findPreference(InternalValues.FORCE_BUILT_IN_EMOJI).setSummary(getString(R.string.preferences__internal_current_version_builtin)); + } + }); + } + + @Override + public void onPause() { + super.onPause(); + SignalExecutors.BOUNDED.execute(EmojiSource::refresh); } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 298079423..7a7f4381e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2418,6 +2418,10 @@ Force censorship Force the app to behave as if it is in a country where Signal is censored. Conversations and Shortcuts + Emoji + Use built-in emoji set. + Current version: Built-In + Current version: %1$d at density %2$s Delete all dynamic shortcuts Click to delete all dynamic shortcuts Disable Profile Sharing diff --git a/app/src/main/res/xml/preferences_internal.xml b/app/src/main/res/xml/preferences_internal.xml index 16dfaed2c..6ce1205c8 100644 --- a/app/src/main/res/xml/preferences_internal.xml +++ b/app/src/main/res/xml/preferences_internal.xml @@ -39,8 +39,8 @@ android:title="@string/preferences__internal_display"> @@ -124,4 +124,13 @@ + + + + + + diff --git a/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.java b/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.java index 38d718e00..586ed8e89 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.java +++ b/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.java @@ -19,6 +19,8 @@ import org.robolectric.annotation.Config; import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.emoji.EmojiSource; +import org.thoughtcrime.securesms.keyvalue.InternalValues; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import java.io.IOException; import java.util.Arrays; @@ -31,7 +33,7 @@ import static org.mockito.Mockito.mock; @RunWith(ParameterizedRobolectricTestRunner.class) @Config(manifest = Config.NONE, application = Application.class) @PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "androidx.*" }) -@PrepareForTest({ApplicationDependencies.class, AttachmentSecretProvider.class}) +@PrepareForTest({ApplicationDependencies.class, AttachmentSecretProvider.class, SignalStore.class, InternalValues.class}) public class EmojiUtilTest_isEmoji { public @Rule PowerMockRule rule = new PowerMockRule(); @@ -68,13 +70,16 @@ public class EmojiUtilTest_isEmoji { } @Test - public void isEmoji() { + public void isEmoji() throws Exception { Context context = ApplicationProvider.getApplicationContext(); PowerMockito.mockStatic(ApplicationDependencies.class); PowerMockito.when(ApplicationDependencies.getApplication()).thenReturn((Application) context); PowerMockito.mockStatic(AttachmentSecretProvider.class); PowerMockito.when(AttachmentSecretProvider.getInstance(any())).thenThrow(IOException.class); + PowerMockito.whenNew(SignalStore.class).withAnyArguments().thenReturn(null); + PowerMockito.mockStatic(SignalStore.class); + PowerMockito.when(SignalStore.internalValues()).thenReturn(PowerMockito.mock(InternalValues.class)); EmojiSource.refresh(); assertEquals(output, EmojiUtil.isEmoji(context, input));