Always include english translations for emoji search.

Updates the `emoji_search` table by including English emoji labels alongside existing localized labels, enabling users to search for emojis in both their preferred language and English.
This commit is contained in:
jeffrey-signal
2025-09-12 11:41:42 -04:00
committed by Greyson Parrelli
parent 23b7ea90a1
commit f0df1b99e5
5 changed files with 90 additions and 33 deletions

View File

@@ -93,21 +93,26 @@ class EmojiSearchTable(context: Context, databaseHelper: SignalDatabase) : Datab
/**
* Deletes the content of the current search index and replaces it with the new one.
*/
fun setSearchIndex(searchIndex: List<EmojiSearchData>) {
val db = databaseHelper.signalReadableDatabase
db.withinTransaction {
fun setSearchIndex(
localizedSearchIndex: List<EmojiSearchData>,
englishSearchIndex: List<EmojiSearchData>
) {
databaseHelper.signalReadableDatabase.withinTransaction { db ->
db.delete(TABLE_NAME, null, null)
db.insert(localizedSearchIndex)
db.insert(englishSearchIndex)
}
}
for (searchData in searchIndex) {
for (label in searchData.tags) {
val values = contentValuesOf(
LABEL to label,
EMOJI to searchData.emoji,
RANK to if (searchData.rank == 0) Int.MAX_VALUE else searchData.rank
)
db.insert(TABLE_NAME, null, values)
}
private fun SQLiteDatabase.insert(searchIndex: List<EmojiSearchData>) {
for (searchData in searchIndex) {
for (label in searchData.tags) {
val values = contentValuesOf(
LABEL to label,
EMOJI to searchData.emoji,
RANK to if (searchData.rank == 0) Int.MAX_VALUE else searchData.rank
)
insert(TABLE_NAME, null, values)
}
}
}

View File

@@ -32,19 +32,20 @@ public final class EmojiSearchIndexDownloadJob extends BaseJob {
private static final String TAG = Log.tag(EmojiSearchIndexDownloadJob.class);
public static final String KEY = "EmojiSearchIndexDownloadJob";
public static final String KEY = "EmojiSearchIndexDownloadJob";
public static final String LANGUAGE_CODE_ENGLISH = "en";
private static final long INTERVAL_WITHOUT_INDEX = TimeUnit.DAYS.toMillis(1);
private static final long INTERVAL_WITH_INDEX = TimeUnit.DAYS.toMillis(7);
private EmojiSearchIndexDownloadJob() {
this(new Parameters.Builder()
.setQueue("EmojiSearchIndexDownloadJob")
.setMaxInstancesForFactory(2)
.addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(Parameters.UNLIMITED)
.build());
.setQueue("EmojiSearchIndexDownloadJob")
.setMaxInstancesForFactory(2)
.addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(Parameters.UNLIMITED)
.build());
}
private EmojiSearchIndexDownloadJob(@NonNull Parameters parameters) {
@@ -99,14 +100,20 @@ public final class EmojiSearchIndexDownloadJob extends BaseJob {
}
Log.i(TAG, "Need to get a new search index. Downloading version: " + manifest.getVersion() + ", language: " + remoteLanguage);
List<EmojiSearchData> localizedSearchIndex = downloadSearchIndex(manifest.getVersion(), remoteLanguage);
List<EmojiSearchData> searchIndex = downloadSearchIndex(manifest.getVersion(), remoteLanguage);
List<EmojiSearchData> englishSearchIndex;
if (remoteLanguage.equals(LANGUAGE_CODE_ENGLISH) || remoteLanguage.startsWith(LANGUAGE_CODE_ENGLISH + "_")) {
englishSearchIndex = Collections.emptyList();
} else {
englishSearchIndex = downloadSearchIndex(manifest.getVersion(), LANGUAGE_CODE_ENGLISH);
}
if (searchIndex.isEmpty()) {
if (localizedSearchIndex.isEmpty()) {
throw new IOException("Emoji search data is empty");
}
SignalDatabase.emojiSearch().setSearchIndex(searchIndex);
SignalDatabase.emojiSearch().setSearchIndex(localizedSearchIndex, englishSearchIndex);
SignalStore.emoji().onSearchIndexUpdated(manifest.getVersion(), remoteLanguage);
SignalStore.emoji().setLastSearchIndexCheck(System.currentTimeMillis());
@@ -153,9 +160,9 @@ public final class EmojiSearchIndexDownloadJob extends BaseJob {
if (parentLanguage != null) {
Log.i(TAG, "No exact match found. Using parent language: " + parentLanguage);
return parentLanguage;
} else if (languages.contains("en")) {
Log.w(TAG, "No match, so falling back to en locale.");
return "en";
} else if (languages.contains(LANGUAGE_CODE_ENGLISH)) {
Log.w(TAG, "No match, so falling back to " + LANGUAGE_CODE_ENGLISH + " locale.");
return LANGUAGE_CODE_ENGLISH;
} else if (languages.contains("en_US")) {
Log.w(TAG, "No match, so falling back to en_US locale.");
return "en_US";

View File

@@ -70,6 +70,7 @@ import org.thoughtcrime.securesms.migrations.DirectoryRefreshMigrationJob;
import org.thoughtcrime.securesms.migrations.DuplicateE164MigrationJob;
import org.thoughtcrime.securesms.migrations.E164FormattingMigrationJob;
import org.thoughtcrime.securesms.migrations.EmojiDownloadMigrationJob;
import org.thoughtcrime.securesms.migrations.EmojiSearchEnglishLabelsMigrationJob;
import org.thoughtcrime.securesms.migrations.EmojiSearchIndexCheckMigrationJob;
import org.thoughtcrime.securesms.migrations.FixChangeNumberErrorMigrationJob;
import org.thoughtcrime.securesms.migrations.GooglePlayBillingPurchaseTokenMigrationJob;
@@ -316,6 +317,7 @@ public final class JobManagerFactories {
put(DuplicateE164MigrationJob.KEY, new DuplicateE164MigrationJob.Factory());
put(E164FormattingMigrationJob.KEY, new E164FormattingMigrationJob.Factory());
put(EmojiDownloadMigrationJob.KEY, new EmojiDownloadMigrationJob.Factory());
put(EmojiSearchEnglishLabelsMigrationJob.KEY, new EmojiSearchEnglishLabelsMigrationJob.Factory());
put(EmojiSearchIndexCheckMigrationJob.KEY, new EmojiSearchIndexCheckMigrationJob.Factory());
put(FixChangeNumberErrorMigrationJob.KEY, new FixChangeNumberErrorMigrationJob.Factory());
put(GooglePlayBillingPurchaseTokenMigrationJob.KEY, new GooglePlayBillingPurchaseTokenMigrationJob.Factory());

View File

@@ -188,11 +188,12 @@ public class ApplicationMigrations {
static final int RESET_ARCHIVE_TIER = 144;
static final int ARCHIVE_BACKUP_ID = 145;
static final int QUOTE_THUMBNAIL_BACKFILL = 146;
static final int EMOJI_ENGLISH_SEARCH = 147;
}
public static final int CURRENT_VERSION = 146;
public static final int CURRENT_VERSION = 147;
/**
/**
* This *must* be called after the {@link JobManager} has been instantiated, but *before* the call
* to {@link JobManager#beginJobLoop()}. Otherwise, other non-migration jobs may have started
* executing before we add the migration jobs.
@@ -505,7 +506,7 @@ public class ApplicationMigrations {
}
if (lastSeenVersion < Version.CHANGE_NUMBER_CAPABILITY_4) {
jobs.put(Version.CHANGE_NUMBER_CAPABILITY_4,new AttributesMigrationJob());
jobs.put(Version.CHANGE_NUMBER_CAPABILITY_4, new AttributesMigrationJob());
}
// if (lastSeenVersion < Version.KBS_MIGRATION) {
@@ -686,11 +687,11 @@ public class ApplicationMigrations {
}
if (lastSeenVersion < Version.SELF_REGISTERTED_STATE) {
jobs.put(Version.SELF_REGISTERTED_STATE, new SelfRegisteredStateMigrationJob());
jobs.put(Version.SELF_REGISTERTED_STATE, new SelfRegisteredStateMigrationJob());
}
if (lastSeenVersion < Version.SVR2_ENCLAVE_UPDATE) {
jobs.put(Version.SVR2_ENCLAVE_UPDATE, new Svr2MirrorMigrationJob());
jobs.put(Version.SVR2_ENCLAVE_UPDATE, new Svr2MirrorMigrationJob());
}
if (lastSeenVersion < Version.STORAGE_LOCAL_UNKNOWNS_FIX) {
@@ -754,7 +755,7 @@ public class ApplicationMigrations {
}
if (lastSeenVersion < Version.SVR2_ENCLAVE_UPDATE_2) {
jobs.put(Version.SVR2_ENCLAVE_UPDATE_2, new Svr2MirrorMigrationJob());
jobs.put(Version.SVR2_ENCLAVE_UPDATE_2, new Svr2MirrorMigrationJob());
}
if (lastSeenVersion < Version.WALLPAPER_MIGRATION_CLEANUP) {
@@ -816,7 +817,7 @@ public class ApplicationMigrations {
if (lastSeenVersion < Version.AVATAR_COLOR_MIGRATION_JOB) {
jobs.put(Version.AVATAR_COLOR_MIGRATION_JOB, new AvatarColorStorageServiceMigrationJob());
}
if (lastSeenVersion < Version.DUPLICATE_E164_FIX_2) {
jobs.put(Version.DUPLICATE_E164_FIX_2, new DuplicateE164MigrationJob());
}
@@ -869,6 +870,10 @@ public class ApplicationMigrations {
jobs.put(Version.QUOTE_THUMBNAIL_BACKFILL, new QuoteThumbnailBackfillMigrationJob());
}
if (lastSeenVersion < Version.EMOJI_ENGLISH_SEARCH) {
jobs.put(Version.EMOJI_ENGLISH_SEARCH, new EmojiSearchEnglishLabelsMigrationJob());
}
return jobs;
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.migrations
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
/**
* Schedules job to download both the localized and English emoji search indices, ensuring that emoji search data is available in the user's preferred
* language as well as English.
*/
internal class EmojiSearchEnglishLabelsMigrationJob(parameters: Parameters = Parameters.Builder().build()) : MigrationJob(parameters) {
companion object {
const val KEY = "EmojiSearchEnglishLabelsMigrationJob"
}
override fun getFactoryKey(): String = KEY
override fun isUiBlocking(): Boolean = false
override fun performMigration() {
if (EmojiSearchIndexDownloadJob.LANGUAGE_CODE_ENGLISH != SignalStore.emoji.searchLanguage) {
SignalStore.emoji.clearSearchIndexMetadata()
EmojiSearchIndexDownloadJob.scheduleImmediately()
}
}
override fun shouldRetry(e: Exception): Boolean = false
class Factory : Job.Factory<EmojiSearchEnglishLabelsMigrationJob> {
override fun create(parameters: Parameters, serializedData: ByteArray?): EmojiSearchEnglishLabelsMigrationJob {
return EmojiSearchEnglishLabelsMigrationJob(parameters)
}
}
}