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

View File

@@ -33,6 +33,7 @@ public final class EmojiSearchIndexDownloadJob extends BaseJob {
private static final String TAG = Log.tag(EmojiSearchIndexDownloadJob.class); 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_WITHOUT_INDEX = TimeUnit.DAYS.toMillis(1);
private static final long INTERVAL_WITH_INDEX = TimeUnit.DAYS.toMillis(7); private static final long INTERVAL_WITH_INDEX = TimeUnit.DAYS.toMillis(7);
@@ -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); 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"); 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().onSearchIndexUpdated(manifest.getVersion(), remoteLanguage);
SignalStore.emoji().setLastSearchIndexCheck(System.currentTimeMillis()); SignalStore.emoji().setLastSearchIndexCheck(System.currentTimeMillis());
@@ -153,9 +160,9 @@ public final class EmojiSearchIndexDownloadJob extends BaseJob {
if (parentLanguage != null) { if (parentLanguage != null) {
Log.i(TAG, "No exact match found. Using parent language: " + parentLanguage); Log.i(TAG, "No exact match found. Using parent language: " + parentLanguage);
return parentLanguage; return parentLanguage;
} else if (languages.contains("en")) { } else if (languages.contains(LANGUAGE_CODE_ENGLISH)) {
Log.w(TAG, "No match, so falling back to en locale."); Log.w(TAG, "No match, so falling back to " + LANGUAGE_CODE_ENGLISH + " locale.");
return "en"; return LANGUAGE_CODE_ENGLISH;
} else if (languages.contains("en_US")) { } else if (languages.contains("en_US")) {
Log.w(TAG, "No match, so falling back to en_US locale."); Log.w(TAG, "No match, so falling back to en_US locale.");
return "en_US"; 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.DuplicateE164MigrationJob;
import org.thoughtcrime.securesms.migrations.E164FormattingMigrationJob; import org.thoughtcrime.securesms.migrations.E164FormattingMigrationJob;
import org.thoughtcrime.securesms.migrations.EmojiDownloadMigrationJob; import org.thoughtcrime.securesms.migrations.EmojiDownloadMigrationJob;
import org.thoughtcrime.securesms.migrations.EmojiSearchEnglishLabelsMigrationJob;
import org.thoughtcrime.securesms.migrations.EmojiSearchIndexCheckMigrationJob; import org.thoughtcrime.securesms.migrations.EmojiSearchIndexCheckMigrationJob;
import org.thoughtcrime.securesms.migrations.FixChangeNumberErrorMigrationJob; import org.thoughtcrime.securesms.migrations.FixChangeNumberErrorMigrationJob;
import org.thoughtcrime.securesms.migrations.GooglePlayBillingPurchaseTokenMigrationJob; import org.thoughtcrime.securesms.migrations.GooglePlayBillingPurchaseTokenMigrationJob;
@@ -316,6 +317,7 @@ public final class JobManagerFactories {
put(DuplicateE164MigrationJob.KEY, new DuplicateE164MigrationJob.Factory()); put(DuplicateE164MigrationJob.KEY, new DuplicateE164MigrationJob.Factory());
put(E164FormattingMigrationJob.KEY, new E164FormattingMigrationJob.Factory()); put(E164FormattingMigrationJob.KEY, new E164FormattingMigrationJob.Factory());
put(EmojiDownloadMigrationJob.KEY, new EmojiDownloadMigrationJob.Factory()); put(EmojiDownloadMigrationJob.KEY, new EmojiDownloadMigrationJob.Factory());
put(EmojiSearchEnglishLabelsMigrationJob.KEY, new EmojiSearchEnglishLabelsMigrationJob.Factory());
put(EmojiSearchIndexCheckMigrationJob.KEY, new EmojiSearchIndexCheckMigrationJob.Factory()); put(EmojiSearchIndexCheckMigrationJob.KEY, new EmojiSearchIndexCheckMigrationJob.Factory());
put(FixChangeNumberErrorMigrationJob.KEY, new FixChangeNumberErrorMigrationJob.Factory()); put(FixChangeNumberErrorMigrationJob.KEY, new FixChangeNumberErrorMigrationJob.Factory());
put(GooglePlayBillingPurchaseTokenMigrationJob.KEY, new GooglePlayBillingPurchaseTokenMigrationJob.Factory()); put(GooglePlayBillingPurchaseTokenMigrationJob.KEY, new GooglePlayBillingPurchaseTokenMigrationJob.Factory());

View File

@@ -188,9 +188,10 @@ public class ApplicationMigrations {
static final int RESET_ARCHIVE_TIER = 144; static final int RESET_ARCHIVE_TIER = 144;
static final int ARCHIVE_BACKUP_ID = 145; static final int ARCHIVE_BACKUP_ID = 145;
static final int QUOTE_THUMBNAIL_BACKFILL = 146; 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 * This *must* be called after the {@link JobManager} has been instantiated, but *before* the call
@@ -505,7 +506,7 @@ public class ApplicationMigrations {
} }
if (lastSeenVersion < Version.CHANGE_NUMBER_CAPABILITY_4) { 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) { // if (lastSeenVersion < Version.KBS_MIGRATION) {
@@ -869,6 +870,10 @@ public class ApplicationMigrations {
jobs.put(Version.QUOTE_THUMBNAIL_BACKFILL, new QuoteThumbnailBackfillMigrationJob()); jobs.put(Version.QUOTE_THUMBNAIL_BACKFILL, new QuoteThumbnailBackfillMigrationJob());
} }
if (lastSeenVersion < Version.EMOJI_ENGLISH_SEARCH) {
jobs.put(Version.EMOJI_ENGLISH_SEARCH, new EmojiSearchEnglishLabelsMigrationJob());
}
return jobs; 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)
}
}
}